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

org.psjava.ds.map.hashtable.OpenAddressingHashTable Maven / Gradle / Ivy

There is a newer version: 0.1.19
Show newest version
package org.psjava.ds.map.hashtable;

import java.util.Iterator;

import org.psjava.ds.map.MapEntry;
import org.psjava.ds.map.MutableMap;
import org.psjava.util.AssertStatus;
import org.psjava.util.ConvertedDataIterator;
import org.psjava.util.DataConverter;
import org.psjava.util.DataFilter;
import org.psjava.util.FilteredIterator;
import org.psjava.util.Java1DArray;
import org.psjava.util.VarargsIterator;

public class OpenAddressingHashTable implements MutableMap {

	/**
	 * This is not very fast map. Because it's structure to provide flexibility of probing.
	 */

	private static class Entry implements MapEntry {
		K keyOrNull; // if null then the entry is lazy deleted.
		V value;
		int keyHash;

		Entry(K key, V value, int keyHash) {
			this.keyOrNull = key;
			this.value = value;
			this.keyHash = keyHash;
		}

		@Override
		public K getKey() {
			AssertStatus.assertTrue(keyOrNull != null);
			return keyOrNull;
		}

		@Override
		public V getValue() {
			AssertStatus.assertTrue(keyOrNull != null);
			return value;
		}

		@Override
		public String toString() {
			if (keyOrNull == null)
				return "";
			return keyOrNull + "=" + value;
		}
	}

	private static final int MAX_LOAD_FACTOR2 = 2;

	private final HashProber prober;
	protected Entry[] bucket;
	protected int load = 0;
	protected int lazyDeletedCount = 0;

	public OpenAddressingHashTable(HashProber prober, int reserve) {
		this.prober = prober;
		bucket = Java1DArray.create(Entry.class, calcBucketSize(reserve));
	}

	protected static int calcBucketSize(int reserve) {
		int size = 1;
		while (size / MAX_LOAD_FACTOR2 < reserve)
			size <<= 1;
		return size;
	}

	@Override
	public void clear() {
		bucket = Java1DArray.create(Entry.class, calcBucketSize(1));
		load = 0;
		lazyDeletedCount = 0;
	}

	@Override
	public void put(K key, V value) {
		ensureArraysCapacity(load + 1);
		putToCurrentArray(key, value);
	}

	protected void putToCurrentArray(final K key, final V value) {
		final int keyHash = key.hashCode();
		probe(keyHash, new BucketVisitor() {
			@Override
			public boolean visitAndGetContinuity(int pos) {
				if (bucket[pos] != null) {
					if (isKeyInBucket(pos, key, keyHash)) {
						bucket[pos].keyOrNull = key;
						bucket[pos].value = value;
						return false;
					} else {
						return true;
					}
				} else {
					bucket[pos] = new Entry(key, value, keyHash);
					load++;
					return false;
				}
			}
		});
	}

	private void probe(int keyHash, BucketVisitor visitor) {
		prober.probe(Math.abs(keyHash) % bucket.length, bucket.length, visitor);
	}

	protected void ensureArraysCapacity(int reserve) {
		if (reserve <= bucket.length / MAX_LOAD_FACTOR2)
			return;
		Entry[] oldBucket = bucket;
		bucket = Java1DArray.create(Entry.class, calcBucketSize(reserve));
		load = 0;
		lazyDeletedCount = 0;
		for (Entry v : oldBucket)
			if (v != null && v.keyOrNull != null)
				putToCurrentArray(v.keyOrNull, v.value);
	}

	@Override
	public V get(K key) {
		Entry e = findEntry(key, null);
		AssertStatus.assertTrue(e != null, "key is not in the map");
		return e.value;
	}

	@Override
	public V get(K key, V def) {
		Entry e = findEntry(key, null);
		if (e == null)
			return def;
		return e.value;
	}

	@Override
	public boolean containsKey(K key) {
		return findEntry(key, null) != null;
	}

	@Override
	public void remove(final K key) {
		Entry e = findEntry(key, null);
		if (e != null) {
			e.keyOrNull = null;
			e.value = null;
			lazyDeletedCount++;
		}
	}

	@Override
	public int size() {
		return load - lazyDeletedCount;
	}

	private Entry findResult;

	private Entry findEntry(final K key, Entry def) {
		final int keyHash = key.hashCode();
		findResult = def;
		probe(keyHash, new BucketVisitor() {
			@Override
			public boolean visitAndGetContinuity(int pos) {
				if (bucket[pos] != null) {
					if (isKeyInBucket(pos, key, keyHash)) {
						findResult = bucket[pos];
						return false;
					} else {
						return true;
					}
				} else {
					return false;
				}
			}
		});
		return findResult;
	}

	@Override
	public Iterator> iterator() {
		return ConvertedDataIterator.create(FilteredIterator.create(VarargsIterator.create(bucket), new DataFilter>() {
			@Override
			public boolean isAccepted(Entry v) {
				return v != null && v.keyOrNull != null;
			}
		}), new DataConverter, MapEntry>() {
			@Override
			public MapEntry convert(Entry v) {
				return v;
			}
		});
	}

	private boolean isKeyInBucket(int pos, K key, int keyHash) {
		Entry e = bucket[pos];
		return e.keyOrNull != null && e.keyHash == keyHash && e.keyOrNull.equals(key);
	}

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

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy