/*
* #!
* 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;
}
}
}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: | 974 / 1506 |
| Version history: | 8 change(s) |
| Referenced in: | #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674) |