BUG-4158: optimize ImmutableOffsetMap 19/26119/1
authorRobert Varga <rovarga@cisco.com>
Sat, 22 Aug 2015 22:36:51 +0000 (00:36 +0200)
committerRobert Varga <rovarga@cisco.com>
Thu, 27 Aug 2015 19:59:45 +0000 (21:59 +0200)
This patch providers optimized equals() for both version of OffsetMap.
It also implements an optimized version of MutableOffsetMap.keySet(),
which does not require SimpleEntry instantiation.

Change-Id: I6a65680bbcc7ad37f251431e495cde48f23efb22
Signed-off-by: Robert Varga <rovarga@cisco.com>
common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java
common/util/src/main/java/org/opendaylight/yangtools/util/MutableOffsetMap.java

index 019722b509af3e0bbff16386effc121389de3287..5dcac60cc6977bf3f7371835baa5d11c3b827341 100644 (file)
@@ -18,6 +18,7 @@ import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.util.AbstractSet;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -117,6 +118,50 @@ public class ImmutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> impleme
         return offsets.isEmpty();
     }
 
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+
+        if (o instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) o;
+            if (offsets.equals(om.offsets) && Arrays.deepEquals(objects, om.objects)) {
+                return true;
+            }
+        } else if (o instanceof MutableOffsetMap) {
+            // Let MutableOffsetMap do the actual work.
+            return o.equals(this);
+        } else if (o instanceof Map) {
+            final Map<?, ?> om = (Map<?, ?>)o;
+
+            // Size and key sets have to match
+            if (size() != om.size() || !keySet().equals(om.keySet())) {
+                return false;
+            }
+
+            try {
+                // Ensure all objects are present
+                for (Entry<K, Integer> e : offsets.entrySet()) {
+                    final V v = objectToValue(e.getKey(), objects[e.getValue()]);
+                    if (!v.equals(om.get(e.getKey()))) {
+                        return false;
+                    }
+                }
+            } catch (ClassCastException e) {
+                // Can be thrown by om.get() and indicate we have incompatible key types
+                return false;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
     @Override
     public final boolean containsKey(final Object key) {
         return offsets.containsKey(key);
index a020c89d28be326ac77be0f9ba64c157848ca740..a08bd89257a9281bad09f435815c6de6f7441439 100644 (file)
@@ -127,7 +127,7 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     @Override
     public final V put(final K key, final V value) {
         Preconditions.checkNotNull(value);
-        final Integer offset = offsets.get(key);
+        final Integer offset = offsets.get(Preconditions.checkNotNull(key));
         if (offset == null) {
             final V ret = newKeys.put(key, value);
             if (ret == null) {
@@ -253,6 +253,68 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         return new MutableOffsetMap<K, V>(this);
     }
 
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+
+        if (o instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) o;
+            if (newKeys.isEmpty() && offsets == om.offsets() && Arrays.deepEquals(objects, om.objects())) {
+                return true;
+            }
+        } else if (o instanceof MutableOffsetMap) {
+            final MutableOffsetMap<?, ?> om = (MutableOffsetMap<?, ?>) o;
+            if (offsets == om.offsets && Arrays.deepEquals(objects, om.objects) && newKeys.equals(om.newKeys)) {
+                return true;
+            }
+        } else if (o instanceof Map) {
+            final Map<?, ?> om = (Map<?, ?>)o;
+
+            // Size and key sets have to match
+            if (size() != om.size() || !keySet().equals(om.keySet())) {
+                return false;
+            }
+
+            try {
+                // Ensure all newKeys are present. Note newKeys is guaranteed to
+                // not contain null value.
+                for (Entry<K, V> e : newKeys.entrySet()) {
+                    if (!e.getValue().equals(om.get(e.getKey()))) {
+                        return false;
+                    }
+                }
+
+                // Ensure all objects are present
+                for (Entry<K, Integer> e : offsets.entrySet()) {
+                    final Object obj = objects[e.getValue()];
+                    if (!NO_VALUE.equals(obj)) {
+                        final V v = objectToValue(e.getKey(), obj);
+                        if (!v.equals(om.get(e.getKey()))) {
+                            return false;
+                        }
+                    }
+                }
+            } catch (ClassCastException e) {
+                // Can be thrown by om.get() and indicate we have incompatible key types
+                return false;
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public final Set<K> keySet() {
+        return new KeySet();
+    }
+
     @VisibleForTesting
     boolean needClone() {
         return needClone;
@@ -271,7 +333,13 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
     private final class EntrySet extends AbstractSet<Entry<K, V>> {
         @Override
         public Iterator<Entry<K, V>> iterator() {
-            return new EntrySetIterator();
+            return new AbstractSetIterator<Entry<K, V>>() {
+                @Override
+                public Entry<K, V> next() {
+                    final K key = nextKey();
+                    return new SimpleEntry<>(key, get(key));
+                }
+            };
         }
 
         @Override
@@ -327,17 +395,34 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         }
     }
 
-    private final class EntrySetIterator implements Iterator<Entry<K, V>> {
+    private final class KeySet extends AbstractSet<K> {
+        @Override
+        public Iterator<K> iterator() {
+            return new AbstractSetIterator<K>() {
+                @Override
+                public K next() {
+                    return nextKey();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return MutableOffsetMap.this.size();
+        }
+    }
+
+    private abstract class AbstractSetIterator<E> implements Iterator<E> {
         private final Iterator<Entry<K, Integer>> oldIterator = offsets.entrySet().iterator();
-        private final Iterator<Entry<K, V>> newIterator = newKeys.entrySet().iterator();
+        private final Iterator<K> newIterator = newKeys.keySet().iterator();
         private int expectedModCount = modCount;
         private K currentKey, nextKey;
 
-        EntrySetIterator() {
-            calculateNextKey();
+        AbstractSetIterator() {
+            updateNextKey();
         }
 
-        private void calculateNextKey() {
+        private void updateNextKey() {
             while (oldIterator.hasNext()) {
                 final Entry<K, Integer> e = oldIterator.next();
                 if (!NO_VALUE.equals(objects[e.getValue()])) {
@@ -346,7 +431,7 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
                 }
             }
 
-            nextKey = newIterator.hasNext() ? newIterator.next().getKey() : null;
+            nextKey = newIterator.hasNext() ? newIterator.next() : null;
         }
 
         private void checkModCount() {
@@ -356,26 +441,13 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
         }
 
         @Override
-        public boolean hasNext() {
+        public final boolean hasNext() {
             checkModCount();
             return nextKey != null;
         }
 
         @Override
-        public Entry<K, V> next() {
-            if (nextKey == null) {
-                throw new NoSuchElementException();
-            }
-
-            checkModCount();
-            currentKey = nextKey;
-            calculateNextKey();
-
-            return new SimpleEntry<>(currentKey, get(currentKey));
-        }
-
-        @Override
-        public void remove() {
+        public final void remove() {
             Preconditions.checkState(currentKey != null);
 
             checkModCount();
@@ -391,5 +463,17 @@ public class MutableOffsetMap<K, V> extends AbstractLazyValueMap<K, V> implement
             expectedModCount = ++modCount;
             currentKey = null;
         }
+
+        protected final K nextKey() {
+            if (nextKey == null) {
+                throw new NoSuchElementException();
+            }
+
+            checkModCount();
+            currentKey = nextKey;
+            updateNextKey();
+
+            return currentKey;
+        }
     }
 }