X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=common%2Futil%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Futil%2FImmutableOffsetMap.java;h=f2d34020ca4997342e853072329e33f71eb0d2cc;hb=ed6fff67cb0fed454ea6654752f23dc9f914c04d;hp=019722b509af3e0bbff16386effc121389de3287;hpb=b0b8d9ef5d43eb850a92203345a3e72c6eeaf043;p=yangtools.git diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java index 019722b509..f2d34020ca 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java @@ -16,12 +16,14 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; +import java.util.AbstractMap.SimpleImmutableEntry; import java.util.AbstractSet; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import javax.annotation.Nonnull; @@ -30,18 +32,16 @@ import javax.annotation.Nonnull; * a backing array. This is useful for situations where the same key set is shared across a multitude of maps, as this * class uses a global cache to share the key-to-offset mapping. * - * This map supports creation of value objects on the fly. To achieve that, subclasses should override {@link #valueToObject(Object)}, - * {@link #objectToValue(Object, Object)}, {@link #clone()} and {@link #toModifiableMap()} methods. - * * @param the type of keys maintained by this map * @param the type of mapped values */ @Beta -public class ImmutableOffsetMap extends AbstractLazyValueMap implements Cloneable, UnmodifiableMapPhase, Serializable { +public final class ImmutableOffsetMap implements UnmodifiableMapPhase, Serializable { private static final long serialVersionUID = 1L; private final Map offsets; - private final Object[] objects; + private final V[] objects; + private int hashCode; /** * Construct a new instance backed by specified key-to-offset map and array of objects. @@ -50,7 +50,7 @@ public class ImmutableOffsetMap extends AbstractLazyValueMap impleme * @param objects Array of value object, may not be null. The array is stored as is, the caller * is responsible for ensuring its contents remain unmodified. */ - ImmutableOffsetMap(@Nonnull final Map offsets, @Nonnull final Object[] objects) { + ImmutableOffsetMap(@Nonnull final Map offsets, @Nonnull final V[] objects) { this.offsets = Preconditions.checkNotNull(offsets); this.objects = Preconditions.checkNotNull(objects); Preconditions.checkArgument(offsets.size() == objects.length); @@ -61,7 +61,7 @@ public class ImmutableOffsetMap extends AbstractLazyValueMap impleme * * @param m Instance to share data with, may not be null. */ - protected ImmutableOffsetMap(@Nonnull final ImmutableOffsetMap m) { + ImmutableOffsetMap(@Nonnull final ImmutableOffsetMap m) { this.offsets = m.offsets; this.objects = m.objects; } @@ -78,23 +78,25 @@ public class ImmutableOffsetMap extends AbstractLazyValueMap impleme * @return An isolated, immutable copy of the input map */ @Nonnull public static Map copyOf(@Nonnull final Map m) { - // Prevent a copy - if (m instanceof ImmutableOffsetMap) { + // Prevent a copy. Note that ImmutableMap is not listed here because of its potentially larger keySet overhead. + if (m instanceof ImmutableOffsetMap || m instanceof SharedSingletonMap) { return m; } - // Better-packed + // Familiar and efficient to copy + if (m instanceof MutableOffsetMap) { + return ((MutableOffsetMap) m).toUnmodifiableMap(); + } + final int size = m.size(); if (size == 0) { + // Shares a single object return ImmutableMap.of(); } if (size == 1) { - return ImmutableMap.copyOf(m); - } - - // Familiar and efficient - if (m instanceof MutableOffsetMap) { - return ((MutableOffsetMap) m).toUnmodifiableMap(); + // Efficient single-entry implementation + final Entry e = m.entrySet().iterator().next(); + return SharedSingletonMap.of(e.getKey(), e.getValue()); } final Map offsets = OffsetMapCache.offsetsFor(m.keySet()); @@ -108,83 +110,139 @@ public class ImmutableOffsetMap extends AbstractLazyValueMap impleme } @Override - public final int size() { + public int size() { return offsets.size(); } @Override - public final boolean isEmpty() { + public boolean isEmpty() { return offsets.isEmpty(); } @Override - public final boolean containsKey(final Object key) { + public int hashCode() { + if (hashCode != 0) { + return hashCode; + } + + int result = 0; + for (Entry e : offsets.entrySet()) { + result += e.getKey().hashCode() ^ objects[e.getValue()].hashCode(); + } + + hashCode = result; + return result; + } + + @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 e : offsets.entrySet()) { + if (!objects[e.getValue()].equals(om.get(e.getKey()))) { + return false; + } + } + } catch (ClassCastException e) { + // Can be thrown by om.get() indicating we have incompatible key types + return false; + } + + return true; + } + + return false; + } + + @Override + public boolean containsKey(final Object key) { return offsets.containsKey(key); } @Override - public final boolean containsValue(final Object value) { - @SuppressWarnings("unchecked") - final Object obj = valueToObject((V)value); + public boolean containsValue(final Object value) { for (Object o : objects) { - if (Objects.equals(obj, o)) { + if (value.equals(o)) { return true; } } return false; } - @SuppressWarnings("unchecked") @Override - public final V get(final Object key) { + public V get(final Object key) { final Integer offset = offsets.get(key); - if (offset == null) { - return null; - } + return offset == null ? null : objects[offset]; + } - return objectToValue((K) key, objects[offset]); + @Override + public V remove(final Object key) { + throw new UnsupportedOperationException(); } @Override - public final V remove(final Object key) { + public V put(final K key, final V value) { throw new UnsupportedOperationException(); } @Override - public final void putAll(final Map m) { + public void putAll(final Map m) { throw new UnsupportedOperationException(); } @Override - public final void clear() { + public void clear() { throw new UnsupportedOperationException(); } @Override - public final Set keySet() { + public Set keySet() { return offsets.keySet(); } @Override - public final Set> entrySet() { - return new EntrySet(); + public Collection values() { + return new ConstantArrayCollection<>(objects); } @Override - public MutableOffsetMap toModifiableMap() { - return new MutableOffsetMap<>(this); + public Set> entrySet() { + return new EntrySet(); } @Override - public ImmutableOffsetMap clone() throws CloneNotSupportedException { - return new ImmutableOffsetMap<>(this); + public MutableOffsetMap toModifiableMap() { + return new MutableOffsetMap<>(this); } Map offsets() { return offsets; } - Object[] objects() { + V[] objects() { return objects; } @@ -202,7 +260,7 @@ public class ImmutableOffsetMap extends AbstractLazyValueMap impleme @Override public Entry next() { final Entry e = it.next(); - return new SimpleEntry<>(e.getKey(), objectToValue(e.getKey(), objects[e.getValue()])); + return new SimpleImmutableEntry<>(e.getKey(), objects[e.getValue()]); } }; }