ビクターDurojaiye:
私は、の単純な実装構築しようとしていますHashMap
学習目的のために、Javaでクラスを。作品を焼き直し(どのように私は知っているハッシュマップやハッシュテーブルにプロセスを焼き直し)。
焼き直した場合、内部配列に存在するすべての要素が識別され、彼らは新しい配列に行くところ新しいハッシュ関数に基づいて、そのハッシュを再計算することによって決定することができます。しかし、どのように配列に存在するすべての要素が識別されますか?
すべてのキーを追跡するメカニズムのいくつかの種類があり、または要素を含む内部配列内のインデックスを追跡メカニズムがあるのでしょうか?
(私は私の実装で使用される)の代替は、要素のために、アレイ全体をスキャンすることであろう。多くの時間は、空のバケツをスキャン無駄になるが、これは非効率的であるかもしれません。より良い方法はありますか?
ここに私の実装です。ここでの焦点は、あるrehash(int)
機能。
public class HashMap<T, U> {
private static final int MIN_CAPACITY = 16;
private static final double LOAD_FACTOR = 0.75;
private int mCount = 0;
private HashMapItem<T, U>[] mArray = (HashMapItem<T, U>[]) new HashMapItem[MIN_CAPACITY];
public HashMap() {
}
private void rehash(int newCapacity) {
HashMapItem<T, U>[] newArray = (HashMapItem<T, U>[]) new HashMapItem[newCapacity];
for (HashMapItem<T, U> hashMapItem : mArray) {
if (hashMapItem != null) {
HashMapItem<T, U> currentNode = hashMapItem;
while (currentNode != null) {
putInArray(currentNode.key, currentNode.value, newArray);
currentNode = currentNode.next;
}
}
}
mArray = newArray;
}
private int hashFunction(T key, int arrayCapacity) {
return Math.abs(key.hashCode()) % arrayCapacity;
}
private boolean putInArray(T key, U value, HashMapItem<T, U>[] array) {
boolean duplicateKey = false;
int index = hashFunction(key, array.length);
HashMapItem<T, U> hashMapItem = array[index];
if (hashMapItem == null) array[index] = new HashMapItem<T, U>(key, value);
else {
HashMapItem<T, U> currentNode = hashMapItem;
while (true) {
if (currentNode.key.equals(key)) {
currentNode.value = value;
duplicateKey = true;
break;
}
else if (currentNode.next != null) currentNode = currentNode.next;
else break;
}
if (!duplicateKey) currentNode.next = new HashMapItem<T, U>(key, value);
}
return duplicateKey;
}
public void put(T key, U value) {
if (mCount >= mArray.length * LOAD_FACTOR) rehash(mArray.length << 1);
boolean duplicateKey = putInArray(key, value, mArray);
if (!duplicateKey) mCount++;
}
public U get(T key) {
int index = hashFunction(key, mArray.length);
HashMapItem<T, U> hashMapItem = mArray[index];
if (hashMapItem != null) {
HashMapItem<T, U> currentNode = hashMapItem;
while (currentNode != null) {
if (currentNode.key.equals(key)) return currentNode.value;
currentNode = currentNode.next;
}
}
return null;
}
public U remove(T key) {
U removedItem = null;
int index = hashFunction(key, mArray.length);
HashMapItem<T, U> hashMapItem = mArray[index];
if (hashMapItem != null) {
HashMapItem<T, U> currentNode = hashMapItem;
HashMapItem<T, U> previousNode = null;
while (currentNode != null) {
if (currentNode.key.equals(key)) {
removedItem = currentNode.value;
if (previousNode == null) mArray[index] = currentNode.next;
else previousNode.next = currentNode.next;
break;
}
previousNode = currentNode;
currentNode = currentNode.next;
}
}
if (removedItem != null) mCount--;
return removedItem;
}
public int count() {
return mCount;
}
private class HashMapItem<T, U> {
T key;
U value;
HashMapItem<T, U> next;
public HashMapItem(T key, U value) {
this.key = key;
this.value = value;
}
}
}
dasblinkenlight:
この問題には2つの方法があります。
- リンクリストのような非空のバケットの構造を維持する -これは合理的に効率的に行うことができます。それはまた、あなたに似た反復の予測可能性を与えることができ
LinkedHashMap
、または - 再ハッシュ上のすべての場所をスキャン -これは、あなたがやっているまさにです。
事実上、選択は、CPUの使用削減のためのメモリを支払うに沸きます。あなたは、多くの場合、ハッシュマップを繰り返す必要がある場合は、最初のソリューションは、より良いです。あなたは焼き直した場合にのみ、あなたがそれを行う場合は、あなたのマップが比較的満杯である場合にのみ、再ハッシュが起こるので、第二の溶液は、より良いです。言い換えれば、スキャン時のチェックの大半は成功しようとしています。