All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gw.util.SpaceEfficientHashMap Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.util;

import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.AbstractMap;

@SuppressWarnings({"unchecked"})
public class SpaceEfficientHashMap implements Map {
  private static final Object EMPTY_KEY = new Object();

  private Object _keys = EMPTY_KEY;
  private Object _values;
  private static final int MAX_LIST_SIZE = 16;

  /**
   * The number of times this HashMap has been structurally modified
   * Structural modifications are those that change the number of mappings in
   * the HashMap or otherwise modify its internal structure (e.g.,
   * rehash).  This field is used to make iterators on Collection-views of
   * the HashMap fail-fast.  (See ConcurrentModificationException).
   */
  transient volatile int modCount;

  public SpaceEfficientHashMap() {
  }

  /**
   * Constructs a new HashMap with the same mappings as the
   * specified Map.  The HashMap is created with
   * default load factor (0.75) and an initial capacity sufficient to
   * hold the mappings in the specified Map.
   *
   * @param m the map whose mappings are to be placed in this map
   * @throws NullPointerException if the specified map is null
   */
  public SpaceEfficientHashMap(Map m) {
    putAll(m);
  }

  @Override
  public int size() {
    if (isObject()) {
      return _keys == EMPTY_KEY ? 0 : 1;
    } else if (isList()) {
      return ((List) _keys).size();
    }
    return ((Map) _keys).size();
  }

  @Override
  public boolean isEmpty() {
    return size() == 0;
  }

  @Override
  public boolean containsKey(Object key) {
    if (isMap()) {
      return ((Map) _keys).containsKey(key);
    } else if (isList()) {
      List keyList = (List) _keys;
      return keyList.contains(key);
    } else {
      return GosuObjectUtil.equals(_keys, key);
    }
  }

  @Override
  public boolean containsValue(Object value) {
    if (isMap()) {
      return ((Map) _keys).containsValue(value);
    } else if (isList()) {
      List valueList = (List) _values;
      return valueList.contains(value);
    } else {
      return _keys != EMPTY_KEY && GosuObjectUtil.equals(_values, value);
    }
  }

  @Override
  public V get(Object key) {
    if (isMap()) {
      return (V) ((Map) _keys).get(key);
    } else if (isList()) {
      List keyList = (List) _keys;
      for (int i = 0; i < keyList.size(); i++) {
        Object o = keyList.get(i);
        if (GosuObjectUtil.equals(o, key)) {
          return (V) ((List) _values).get(i);
        }
      }

      return null;
    } else {
      return _keys != EMPTY_KEY && GosuObjectUtil.equals(_keys, key) ? (V) _values : null;
    }
  }

  @Override
  public V put(K key, V value) {
    if (isMap()) {
      return (V) ((Map) _keys).put(key, value);
    } else if (isList()) {
      ArrayList keyList = (ArrayList) _keys;
      ArrayList valueList = (ArrayList) _values;
      for (int i = 0; i < keyList.size(); i++) {
        Object o = keyList.get(i);
        if (GosuObjectUtil.equals(o, key)) {
          V oldValue = (V) valueList.get(i);
          valueList.set(i, value);
          return oldValue;
        }
      }

      if (keyList.size() + 1 > MAX_LIST_SIZE) {
        Map map = new HashMap(keyList.size() + 1);
        for (int i = 0; i < keyList.size(); i++) {
          map.put(keyList.get(i), valueList.get(i));
        }
        map.put(key, value);
        _keys = map;
        _values = null;
      } else {
        keyList.add(key);
        valueList.add(value);
      }
      return null;
    } else {
      if (GosuObjectUtil.equals(_keys, key)) {
        V oldValue = (V) _values;
        _values = value;
        return oldValue;
      } else if (_keys == EMPTY_KEY) {
        _keys = key;
        _values = value;
        return null;
      } else {
        List keyList = new ArrayList(2);
        keyList.add(_keys);
        keyList.add(key);
        List valueList = new ArrayList(2);
        valueList.add(_values);
        valueList.add(value);
        _keys = keyList;
        _values = valueList;

        return null;
      }
    }
  }

  @Override
  public V remove(Object key) {
    if (isMap()) {
      return (V) ((Map) _keys).remove(key);
    } else if (isList()) {
      ArrayList keyList = (ArrayList) _keys;
      ArrayList valueList = (ArrayList) _values;
      for (int i = 0; i < keyList.size(); i++) {
        Object o = keyList.get(i);
        if (GosuObjectUtil.equals(o, key)) {
          V oldValue = (V) valueList.get(i);
          // The next two lines are expensive...
          keyList.remove(i);
          valueList.remove(i);

          return oldValue;
        }
      }
      return null;
    } else {
      if (GosuObjectUtil.equals(_keys, key)) {
        V oldValue = (V) _values;
        _values = null;
        _keys = EMPTY_KEY;
        return oldValue;
      } else {
        return null;
      }
    }
  }

  @Override
  public void putAll(Map m) {
    if (m.isEmpty()) {
      return;
    }
    if (isMap()) {
      ((Map) _keys).putAll(m);
    } else {
      if (m.size() < 10) {
        for (Entry entry : m.entrySet()) {
          put(entry.getKey(), entry.getValue());
        }
      } else {
        Map map = new HashMap();
        if (isList()) {
          ArrayList keyList = (ArrayList) _keys;
          ArrayList valueList = (ArrayList) _values;
          for (int i = 0; i < keyList.size(); i++) {
            map.put(keyList.get(i), valueList.get(i));
          }
        } else if (_keys != EMPTY_KEY) {
          map.put(_keys, _values);
        }
        map.putAll(m);
        _keys = map;
        _values = null;
        trimToSize();
      }
    }
  }

  @Override
  public void clear() {
    _keys = EMPTY_KEY;
    _values = null;
  }

  @Override
  public Set keySet() {
    if (isMap()) {
      return ((Map) _keys).keySet();
    } else {
      return new MyListSet(true);
    }
  }

  @Override
  public Collection values() {
    if (isMap()) {
      return ((Map) _keys).values();
    } else {
      return new MyListSet(false);
    }
  }

  @Override
  public Set> entrySet() {
    if (isMap()) {
      return ((Map) _keys).entrySet();
    } else {
      return new MyEntrySet();
    }
  }

  /**
   * Compares the specified object with this map for equality.  Returns
   * true if the given object is also a map and the two maps
   * represent the same mappings.  More formally, two maps m1 and
   * m2 represent the same mappings if
   * m1.entrySet().equals(m2.entrySet()).  This ensures that the
   * equals method works properly across different implementations
   * of the Map interface.
   * 

*

This implementation first checks if the specified object is this map; * if so it returns true. Then, it checks if the specified * object is a map whose size is identical to the size of this map; if * not, it returns false. If so, it iterates over this map's * entrySet collection, and checks that the specified map * contains each mapping that this map contains. If the specified map * fails to contain such a mapping, false is returned. If the * iteration completes, true is returned. * * @param o object to be compared for equality with this map * @return true if the specified object is equal to this map */ public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Map)) { return false; } Map m = (Map) o; if (m.size() != size()) { return false; } try { Iterator> i = entrySet().iterator(); while (i.hasNext()) { Entry e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(m.get(key) == null && m.containsKey(key))) { return false; } } else { if (!value.equals(m.get(key))) { return false; } } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } /** * Returns the hash code value for this map. The hash code of a map is * defined to be the sum of the hash codes of each entry in the map's * entrySet() view. This ensures that m1.equals(m2) * implies that m1.hashCode()==m2.hashCode() for any two maps * m1 and m2, as required by the general contract of * {@link Object#hashCode}. *

*

This implementation iterates over entrySet(), calling * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the * set, and adding up the results. * * @return the hash code value for this map * @see Map.Entry#hashCode() * @see Object#equals(Object) * @see Set#equals(Object) */ public int hashCode() { int h = 0; Iterator> i = entrySet().iterator(); while (i.hasNext()) { h += i.next().hashCode(); } return h; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("{"); int i = 0; for (Entry entry : entrySet()) { if (i > 0) { buf.append(", "); } buf.append(entry.getKey()).append("=").append(entry.getValue()); i++; } buf.append("}"); return buf.toString(); } private boolean isObject() { return !isList() && !isMap(); } private boolean isList() { return _keys instanceof List; } private boolean isMap() { return _keys instanceof Map; } public void trimToSize() { if (isList()) { ArrayList keyList = (ArrayList) _keys; ArrayList valueList = (ArrayList) _values; if (keyList.size() == 0) { _keys = EMPTY_KEY; _values = null; } else if (keyList.size() == 1) { _keys = keyList.get(0); _values = valueList.get(0); } else { keyList.trimToSize(); valueList.trimToSize(); } } else if (isMap()) { Map map = (Map) _keys; if (map.size() == 0) { // compact to object _keys = EMPTY_KEY; _values = null; } else if (map.size() == 1) { Map.Entry entry = (Entry) map.entrySet().iterator().next(); _keys = entry.getKey(); _values = entry.getValue(); } else if (map.size() <= MAX_LIST_SIZE) { ArrayList keyList = new ArrayList(map.size()); ArrayList valueList = new ArrayList(map.size()); for (Object o : map.entrySet()) { Map.Entry entry = (Entry) o; keyList.add(entry.getKey()); valueList.add(entry.getValue()); } keyList.trimToSize(); valueList.trimToSize(); _keys = keyList; _values = valueList; } else { // can't compact a map. Too bad, so sad. Or we could reallocate the map... _keys = new HashMap(map); } } } private final class MyListSet extends AbstractSet { boolean _returnKeySet; MyListSet(boolean returnKeySet) { _returnKeySet = returnKeySet; } public Iterator iterator() { if (isList()) { return new Iterator() { private Iterator _kit = ((List) _keys).iterator(); private Iterator _vit = ((List) _values).iterator(); @Override public boolean hasNext() { return _kit.hasNext(); } @Override public T next() { Object key = _kit.next(); Object value = _vit.next(); return (T) (_returnKeySet ? key : value); } @Override public void remove() { _kit.remove(); _vit.remove(); } }; } if (_keys == EMPTY_KEY && _values == null) { return new Iterator() { @Override public boolean hasNext() { return false; } @Override public T next() { throw new NoSuchElementException(); } @Override public void remove() { throw new NoSuchElementException(); } }; } else { return new Iterator() { boolean _onFirst = true; @Override public boolean hasNext() { return _onFirst; } @Override public T next() { if (!_onFirst) { throw new NoSuchElementException(); } _onFirst = false; if (_returnKeySet) { return (T) _keys; } else { return (T) _values; } } @Override public void remove() { if (_onFirst) { throw new IllegalStateException(); } _keys = EMPTY_KEY; _values = null; } }; } } public int size() { return isList() ? ((List) _keys).size() : (_keys == EMPTY_KEY) ? 0 : 1; } public boolean contains(Object o) { if (_returnKeySet) { return containsKey(o); } else { return containsValue(o); } } public boolean remove(Object o) { if (_returnKeySet) { return SpaceEfficientHashMap.this.remove(o) != null; } else { Iterator e = iterator(); if (o == null) { while (e.hasNext()) { if (e.next() == null) { e.remove(); return true; } } } else { while (e.hasNext()) { if (o.equals(e.next())) { e.remove(); return true; } } } return false; } } public void clear() { SpaceEfficientHashMap.this.clear(); } } private final class MyEntrySet extends AbstractSet> { MyEntrySet() { } public Iterator> iterator() { if (isList()) { return new Iterator>() { private Iterator _kit = ((List) _keys).iterator(); private Iterator _vit = ((List) _values).iterator(); @Override public boolean hasNext() { return _kit.hasNext(); } @Override public Entry next() { Object key = _kit.next(); Object value = _vit.next(); return new AbstractMap.SimpleEntry((S) key, (T) value); } @Override public void remove() { _kit.remove(); _vit.remove(); } }; } if (_keys == EMPTY_KEY) { return new Iterator>() { @Override public boolean hasNext() { return false; } @Override public Entry next() { throw new NoSuchElementException(); } @Override public void remove() { throw new NoSuchElementException(); } }; } else { return new Iterator>() { boolean _onFirst = true; @Override public boolean hasNext() { return _onFirst; } @Override public Entry next() { if (!_onFirst) { throw new NoSuchElementException(); } _onFirst = false; return new AbstractMap.SimpleEntry((S) _keys, (T) _values); } @Override public void remove() { _keys = null; _values = null; } }; } } public int size() { return isList() ? ((List) _keys).size() : (_keys == EMPTY_KEY) ? 0 : 1; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return SpaceEfficientHashMap.this.remove(o) != null; } public void clear() { SpaceEfficientHashMap.this.clear(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy