Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

390
LINES

< > BotCompany Repo | #1013385 // CompactPairKeyHashMap - map Pair to Object - synchronized

JavaX fragment (include)

/*
 * #!
 * Ontopia Engine
 * #-
 * Copyright (C) 2001 - 2013 The Ontopia Project
 * #-
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * !#
 */

sclass CompactPairKeyHashMap<K, V> extends AbstractMap<Pair<K>, V> {
  final static int INITIAL_SIZE = 3;
  final static double LOAD_FACTOR = 0.6;

  // This object is used to represent null, should clients use that as
  final static Object nullObject = new Object();
  
  /**
   * When a key is deleted this object is put into the hashtable in
   * its place, so that other entries with the same key (collisions)
   * further down the hashtable are not lost after we delete an object
   * in the collision chain.
   */
  final static Object deletedObject = new Object();
  int elements;
  int freecells;
  K[] keys1;
  K[] keys2;
  V[] values; // object at pos x corresponds to key at pos x
  int modCount;

  *() {
    this(INITIAL_SIZE);
  }

  *(int size) {
    keys1 = (K[]) new Object[(size==0 ? 1 : size)];
    keys2 = (K[]) new Object[(size==0 ? 1 : size)];
    values = (V[]) new Object[(size==0 ? 1 : size)];
    elements = 0;
    freecells = keys1.length;
    modCount = 0;
  }

  // ===== MAP IMPLEMENTATION =============================================

  /**
   * Returns the number of key/value mappings in this map.
   */
  public synchronized int size() {
    return elements;
  }
  
  /**
   * Returns <tt>true</tt> if this map contains no mappings.
   */
  public synchronized boolean isEmpty() {
    return elements == 0;
  }

  /**
   * Removes all key/value mappings in the map.
   */
  public synchronized void clear() {
    elements = 0;
    for (int ix = 0; ix < keys1.length; ix++) {
      keys1[ix] = keys2[ix] = null;
      values[ix] = null;
    }
    freecells = values.length;
    modCount++;
  }
  
  /**
   * Returns <tt>true</tt> if this map contains the specified key.
   */
  public synchronized boolean containsKey(Object k) {
    return keys1[findKeyIndex(k)] != null;
  }
  
  /**
   * Returns <tt>true</tt> if this map contains the specified value.
   */
  public synchronized boolean containsValue(Object v) {
    if (v == null)
      v = (V)nullObject;

    for (int ix = 0; ix < values.length; ix++)
      if (values[ix] != null && values[ix].equals(v))
        return true;

    return false;
  }

  /**
   * Returns a read-only set view of the map's keys.
   */
  public synchronized Set<Entry<Pair<K>, V>> entrySet() {
    throw new UnsupportedOperationException();
  }

  /**
   * Removes the mapping with key k, if there is one, and returns its
   * value, if there is one, and null if there is none.
   */
  public synchronized V remove(Object k) {
    int index = findKeyIndex(k);

    // we found the right position, now do the removal
    if (keys1[index] != null) {
      // we found the object

      // same problem here as with put
      V v = values[index];
      keys1[index] = keys2[index] = (K) deletedObject;
      values[index] = (V) deletedObject;
      modCount++;
      elements--;
      return v;
    } else
      // we did not find the key
      return null;
  }

  /**
   * Adds the specified mapping to this map, returning the old value for
   * the mapping, if there was one.
   */
  public synchronized V put(Pair<K> k, V v) {
    if (k.a == null)
      k = pair((K)nullObject, k.b);
    if (k.b == null)
      k = pair(k.a, (K)nullObject);

    int hash = k.hashCode();
    int hash1 = k.a.hashCode();
    int hash2 = k.b.hashCode();
    int index = (hash & 0x7FFFFFFF) % keys1.length;
    int offset = 1;
    int deletedix = -1;
    
    // search for the key (continue while !null and !this key)
    while(keys1[index] != null &&
          !(keys1[index].hashCode() == hash1 &&
            keys2[index].hashCode() == hash2 &&
            keys1[index].equals(k.a) &&
            keys2[index].equals(k.b))) {

      // if there's a deleted mapping here we can put this mapping here,
      // provided it's not in here somewhere else already
      if (keys1[index] == deletedObject)
        deletedix = index;
      
      index = ((index + offset) & 0x7FFFFFFF) % keys1.length;
      offset = offset*2 + 1;

      if (offset == -1)
        offset = 2;
    }
    
    if (keys1[index] == null) { // wasn't present already
      if (deletedix != -1) // reusing a deleted cell
        index = deletedix;
      else
        freecells--;

      modCount++;
      elements++;

      keys1[index] = k.a;
      keys2[index] = k.b;
      values[index] = (V) v;
      
      // rehash with increased capacity
      if (1 - (freecells / (double) keys1.length) > LOAD_FACTOR)
        rehash(keys1.length*2 + 1);
      return null;
    } else { // was there already
      modCount++;
      V oldv = values[index];
      values[index] = (V) v;
      return oldv;
    }
  }

  /**
   * INTERNAL: Rehashes the hashmap to a bigger size.
   */
  void rehash(int newCapacity) {
    int oldCapacity = keys1.length;
    K[] newKeys1 = (K[]) new Object[newCapacity];
    K[] newKeys2 = (K[]) new Object[newCapacity];
    V[] newValues = (V[]) new Object[newCapacity];

    for (int ix = 0; ix < oldCapacity; ix++) {
      Object k1 = keys1[ix];
      if (k1 == null || k1 == deletedObject)
        continue;
      
      Object k2 = keys2[ix];
      int hash = pair(k1, k2).hashCode();
      int index = (hash & 0x7FFFFFFF) % newCapacity;
      int offset = 1;

      // search for the key
      while(newKeys1[index] != null) { // no need to test for duplicates
        index = ((index + offset) & 0x7FFFFFFF) % newCapacity;
        offset = offset*2 + 1;

        if (offset == -1)
          offset = 2;
      }

      newKeys1[index] = (K) k1;
      newKeys2[index] = (K) k2;
      newValues[index] = values[ix];
    }

    keys1 = newKeys1;
    keys2 = newKeys2;
    values = newValues;
    freecells = keys1.length - elements;
  }

  /**
   * Returns the value for the key k, if there is one, and null if
   * there is none.
   */
  public synchronized V get(Object k) {
    return values[findKeyIndex(k)];
  }

  /**
   * Returns a virtual read-only collection containing all the values
   * in the map.
   */
  public synchronized Collection<V> values() {
    return new ValueCollection();
  }

  /**
   * Returns a virtual read-only set of all the keys in the map.
   */
  public synchronized Set<Pair<K>> keySet() {
    return new KeySet();
  }

  // --- Internal utilities

  final int findKeyIndex(Object _k) {
    Pair k = cast _k;
    if (k.a == null)
      k = pair((K)nullObject, k.b);
    if (k.b == null)
      k = pair(k.a, (K)nullObject);

    int hash = k.hashCode();
    int hash1 = k.a.hashCode();
    int hash2 = k.b.hashCode();
    int index = (hash & 0x7FFFFFFF) % keys1.length;
    int offset = 1;
    int deletedix = -1;
    
    // search for the key (continue while !null and !this key)
    while(keys1[index] != null &&
          !(keys1[index].hashCode() == hash1 &&
            keys2[index].hashCode() == hash2 &&
            keys1[index].equals(k.a) &&
            keys2[index].equals(k.b))) {
      index = ((index + offset) & 0x7FFFFFFF) % keys1.length;
      offset = offset*2 + 1;

      if (offset == -1)
        offset = 2;
    }
    return index;
  }
  
  // --- Key set

  class KeySet extends AbstractSet<Pair<K>> {
    public synchronized int size() {
      return elements;
    }

    public synchronized boolean contains(Object k) {
      return containsKey(k);
    }

    public synchronized Iterator<Pair<K>> iterator() {
      return new KeyIterator();
    }
  }

  class KeyIterator implements Iterator<Pair<K>> {
    private int ix;
    
    private KeyIterator() {
      // walk up to first value, so that hasNext() and next() return
      // correct results
      for (; ix < keys1.length; ix++)
        if (values[ix] != null && keys1[ix] != deletedObject)
          break;
    }

    public synchronized boolean hasNext() {
      return ix < keys1.length;
    }

    public synchronized void remove() {
      throw new UnsupportedOperationException("Collection is read-only");
    }

    public synchronized Pair<K> next() {
      if (ix >= keys1.length)
        throw new NoSuchElementException();
      K key1 = (K) keys1[ix];
      K key2 = (K) keys2[ix];
      ix++;
      
      // walk up to next value
      for (; ix < keys1.length; ix++)
        if (keys1[ix] != null && keys1[ix] != deletedObject)
          break;
      
      // ix now either points to next key, or outside array (if no next)
      return pair(key1, key2);
    }
  }
  
  // --- Value collection

  class ValueCollection extends AbstractCollection<V> {
    public synchronized int size() {
      return elements;
    }

    public synchronized Iterator<V> iterator() {
      return new ValueIterator();
    }

    public synchronized boolean contains(Object v) {
      return containsValue(v);
    }
  }

  class ValueIterator implements Iterator<V> {
    private int ix;
    
    private ValueIterator() {
      // walk up to first value, so that hasNext() and next() return
      // correct results
      for (; ix < values.length; ix++)
        if (values[ix] != null && values[ix] != deletedObject)
          break;
    }

    public synchronized boolean hasNext() {
      return ix < values.length;
    }

    public synchronized void remove() {
      throw new UnsupportedOperationException("Collection is read-only");
    }

    public synchronized V next() {
      if (ix >= values.length)
        throw new NoSuchElementException();
      V value = (V) values[ix++];
      
      // walk up to next value
      for (; ix < values.length; ix++)
        if (values[ix] != null && values[ix] != deletedObject)
          break;
      
      // ix now either points to next value, or outside array (if no next)
      return value;
    }
  }
}

Author comment

Began life as a copy of #1013303

download  show line numbers  debug dex  old transpilations   

Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1013385
Snippet name: CompactPairKeyHashMap - map Pair to Object - synchronized
Eternal ID of this version: #1013385/9
Text MD5: 23d7126110be94924140caad240495d7
Author: stefan
Category: javax / collections
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-01-04 00:12:40
Source code size: 10675 bytes / 390 lines
Pitched / IR pitched: No / No
Views / Downloads: 461 / 1028
Version history: 8 change(s)
Referenced in: #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674)