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

org.semanticweb.elk.util.collections.ArrayHashMap Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * elk-reasoner
 * 
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 Oxford University Computing Laboratory
 * %%
 * 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.
 * #L%
 */
/**
 * @author Yevgeny Kazakov, May 23, 2011
 */
package org.semanticweb.elk.util.collections;

import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * hash maps using array and linear probing for resolving hash collision see [1]
 * p.526. Reuses some code from the implementation of HashMap.
 * 
 * [1] Donald E. Knuth, The Art of Computer Programming, Volume 3, Sorting and
 * Searching, Second Edition
 * 
 * @author Yevgeny Kazakov
 * @param 
 *            the type of the keys
 * @param 
 *            the type of the values
 * 
 */
public class ArrayHashMap extends AbstractMap implements Map {

	/**
	 * The table for the keys; the length MUST always be a power of two.
	 */
	protected volatile transient K[] keys;

	/**
	 * The table for the values; the length MUST be equal to the length of keys
	 * and MUST always be a power of two.
	 */
	protected volatile transient V[] values;

	/**
	 * The number of key-value entries contained in this map.
	 */
	protected transient int size;

	@SuppressWarnings("unchecked")
	public ArrayHashMap(int initialCapacity) {
		int capacity = LinearProbing.getInitialCapacity(initialCapacity);
		this.keys = (K[]) new Object[capacity];
		this.values = (V[]) new Object[capacity];
		this.size = 0;
	}

	@SuppressWarnings("unchecked")
	public ArrayHashMap() {
		int capacity = LinearProbing.DEFAULT_INITIAL_CAPACITY;
		this.keys = (K[]) new Object[capacity];
		this.values = (V[]) new Object[capacity];
		this.size = 0;
	}

	@Override
	public int size() {
		return size;
	}

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

	@Override
	public boolean containsKey(Object key) {
		if (key == null)
			throw new NullPointerException();
		return LinearProbing.contains(keys, key);
	}

	@Override
	public boolean containsValue(Object value) {
		if (value == null)
			throw new NullPointerException();
		V[] v = this.values;
		// we could not do much better than linear search
		for (int i = 0; i < keys.length; i++)
			if (value.equals(v[i]))
				return true;
		return false;
	}

	@Override
	public V get(Object key) {
		if (key == null)
			throw new NullPointerException();
		// to avoid problems in the middle of resizing, we copy keys and values
		// when they have the same size
		K[] k;
		V[] v;
		for (;;) {
			k = this.keys;
			v = this.values;
			if (k.length == v.length)
				break;
		}
		int pos = LinearProbing.getPosition(k, key);
		if (k[pos] == null)
			return null;
		// else
		return v[pos];
	}

	/**
	 * Associates the given key with the given value in the map defined by the
	 * keys and value arrays. If an entry with the key equal to the given one
	 * already exists in the map, the value for this key will be overwritten
	 * with the given value.
	 * 
	 * @param keys
	 *            the keys of the map
	 * @param values
	 *            the values of the map
	 * @param key
	 *            the key of the entry
	 * @param value
	 *            the value of the entry
	 * @return the previous value associated with the key or null if
	 *         there was no such a previous value.
	 */
	private static  V putKeyValue(K[] keys, V[] values, K key, V value) {
		int pos = LinearProbing.getPosition(keys, key);
		if (keys[pos] == null) {
			keys[pos] = key;
			values[pos] = value;
			return null;
		}
		// else
		V oldValue = values[pos];
		values[pos] = value;
		return oldValue;
	}

	/**
	 * Remove the entry in the keys and values such that the key of the entry is
	 * equal to the given object according to the equality function.
	 * 
	 * @param keys
	 *            the array of keys
	 * @param values
	 *            the arrays of values
	 * @param key
	 *            the key for which to delete the entry
	 * @return the value of the deleted entry, null if nothing has been
	 *         deleted
	 */
	private static  V removeEntry(K[] keys, V[] values, Object key) {
		int pos = LinearProbing.getPosition(keys, key);
		if (keys[pos] == null)
			return null;
		// else
		V result = values[pos];
		LinearProbing.remove(keys, values, pos);
		return result;
	}

	/**
	 * Increasing the capacity of the map
	 */
	private void enlarge() {
		int oldCapacity = keys.length;
		if (oldCapacity == LinearProbing.MAXIMUM_CAPACITY)
			throw new IllegalArgumentException(
					"Map cannot grow beyond capacity: "
							+ LinearProbing.MAXIMUM_CAPACITY);
		K oldKeys[] = keys;
		V oldValues[] = values;
		int newCapacity = oldCapacity << 1;
		@SuppressWarnings("unchecked")
		K newKeys[] = (K[]) new Object[newCapacity];
		@SuppressWarnings("unchecked")
		V newValues[] = (V[]) new Object[newCapacity];
		for (int i = 0; i < oldCapacity; i++) {
			K key = oldKeys[i];
			if (key != null)
				putKeyValue(newKeys, newValues, key, oldValues[i]);
		}
		this.keys = newKeys;
		this.values = newValues;
	}

	/**
	 * Decreasing the capacity of the map
	 */
	private void shrink() {
		int oldCapacity = keys.length;
		if (oldCapacity <= LinearProbing.DEFAULT_INITIAL_CAPACITY)
			return;
		K oldKeys[] = keys;
		V oldValues[] = values;
		int newCapacity = oldCapacity >> 1;
		@SuppressWarnings("unchecked")
		K newKeys[] = (K[]) new Object[newCapacity];
		@SuppressWarnings("unchecked")
		V newValues[] = (V[]) new Object[newCapacity];
		for (int i = 0; i < oldCapacity; i++) {
			K key = oldKeys[i];
			if (key != null)
				putKeyValue(newKeys, newValues, key, oldValues[i]);
		}
		this.keys = newKeys;
		this.values = newValues;
	}

	@Override
	public V put(K key, V value) {
		if (key == null)
			throw new NullPointerException();
		V result = putKeyValue(keys, values, key, value);
		if (result == null && ++size == LinearProbing.getUpperSize(keys.length))
			enlarge();
		return result;
	}

	@Override
	public V remove(Object key) {
		if (key == null)
			throw new NullPointerException();
		V result = removeEntry(keys, values, key);
		if (result != null && --size == LinearProbing.getLowerSize(keys.length))
			shrink();
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void clear() {
		int capacity = keys.length >> 2;
		if (capacity == 0)
			capacity = 1;
		size = 0;
		this.keys = (K[]) new Object[capacity];
		this.values = (V[]) new Object[capacity];
	}

	@Override
	public Set keySet() {
		return new KeySet();
	}

	@Override
	public Collection values() {
		return new ValueCollection();
	}

	@Override
	public Set> entrySet() {
		return new EntrySet();
	}

	private class KeyIterator extends LinearProbingIterator {

		KeyIterator() {
			super(keys, size);
			init();
		}

		@Override
		void checkSize(int expectedSize) {
			if (expectedSize != size)
				throw new ConcurrentModificationException();
		}

		@Override
		void remove(int pos) {
			LinearProbing.remove(dataSnapshot, values, pos);
			size--;
		}

		@Override
		K getValue(K element, int pos) {
			return element;
		}

	}

	private final class KeySet extends AbstractSet implements
			DirectAccess {

		@Override
		public Iterator iterator() {
			return new KeyIterator();
		}

		@Override
		public boolean contains(Object o) {
			return ArrayHashMap.this.containsKey(o);
		}

		@Override
		public boolean remove(Object o) {
			return ArrayHashMap.this.remove(o) != null;
		}

		@Override
		public int size() {
			return size;
		}

		@Override
		public void clear() {
			ArrayHashMap.this.clear();
		}

		@Override
		public K[] getRawData() {
			return keys;
		}

	}

	private class ValueIterator extends LinearProbingIterator {

		ValueIterator() {
			super(values, size);
			init();
		}

		@Override
		void checkSize(int expectedSize) {
			if (expectedSize != size)
				throw new ConcurrentModificationException();
		}

		@Override
		void remove(int pos) {
			LinearProbing.remove(keys, dataSnapshot, pos);
			size--;
		}

		@Override
		V getValue(V element, int pos) {
			return element;
		}

	}

	private final class ValueCollection extends AbstractCollection implements
			DirectAccess {

		@Override
		public Iterator iterator() {
			return new ValueIterator();
		}

		@Override
		public int size() {
			return size;
		}

		@Override
		public void clear() {
			ArrayHashMap.this.clear();
		}

		@Override
		public V[] getRawData() {
			return values;
		}

	}

	private class EntryIterator extends
			LinearProbingIterator> {

		final V[] valuesSnapshot;

		EntryIterator() {
			super(keys, size);
			this.valuesSnapshot = values;
			init();
		}

		@Override
		void checkSize(int expectedSize) {
			if (expectedSize != size)
				throw new ConcurrentModificationException();
		}

		@Override
		void remove(int pos) {
			LinearProbing.remove(dataSnapshot, values, pos);
			size--;
		}

		@Override
		Map.Entry getValue(K element, int pos) {
			return new Entry(this, pos);
		}
	}

	class Entry implements Map.Entry {
		// copy of the iterator
		final EntryIterator iterator;
		// position of the cursor
		final int cursor;

		Entry(EntryIterator iterator, int cursor) {
			this.iterator = iterator;
			this.cursor = cursor;
		}

		@Override
		public K getKey() {
			return iterator.dataSnapshot[cursor];
		}

		@Override
		public V getValue() {
			return iterator.valuesSnapshot[cursor];
		}

		@Override
		public V setValue(V value) {
			V previous = iterator.valuesSnapshot[cursor];
			iterator.valuesSnapshot[cursor] = value;
			return previous;
		}

	}

	private final class EntrySet extends AbstractSet> {
		@Override
		public Iterator> iterator() {
			return new EntryIterator();
		}

		@Override
		public boolean contains(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Object k = ((Map.Entry) o).getKey();
			return ArrayHashMap.this.containsKey(k);
		}

		@Override
		public boolean remove(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Object k = ((Map.Entry) o).getKey();
			return ArrayHashMap.this.remove(k) != null;
		}

		@Override
		public int size() {
			return size;
		}

		@Override
		public void clear() {
			ArrayHashMap.this.clear();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy