5

私は自分のHashMapを実装しなければならない割り当てに取り組んでいます。割り当てテキストでは、リストの配列として記述されており、要素を追加する場合は常に、配列内の最終的な場所はそのhashCodeによって決定されます。私の場合はスプレッドシートからの位置なので、columnNumber + rowNumberを取得し、それを文字列に変換してから、hashCodeとしてintに変換し、その場所を配列に挿入します。もちろん、Node(key、value)の形式で挿入されます。ここで、keyはセルの位置であり、valueはセルの値です。

しかし、なぜリストの配列が必要なのか理解できないと言わなければなりません。それは、複数の要素を含むリストになってしまった場合、ルックアップ時間が大幅に増えることはないのでしょうか。それで、それはむしろノードの配列であるべきではありませんか?

また、JavaでのHashMapのこの実装を見つけました。

public class HashEntry {
      private int key;
      private int value;

      HashEntry(int key, int value) {
            this.key = key;
            this.value = value;
      }     

      public int getKey() {
            return key;
      }

      public int getValue() {
            return value;
      }
}

public class HashMap {
  private final static int TABLE_SIZE = 128;

  HashEntry[] table;

  HashMap() {
        table = new HashEntry[TABLE_SIZE];
        for (int i = 0; i < TABLE_SIZE; i++)
              table[i] = null;
  }

  public int get(int key) {
        int hash = (key % TABLE_SIZE);
        while (table[hash] != null && table[hash].getKey() != key)
              hash = (hash + 1) % TABLE_SIZE;
        if (table[hash] == null)
              return -1;
        else
              return table[hash].getValue();
  }

  public void put(int key, int value) {
        int hash = (key % TABLE_SIZE);
        while (table[hash] != null && table[hash].getKey() != key)
              hash = (hash + 1) % TABLE_SIZE;
        table[hash] = new HashEntry(key, value);
  }
}

したがって、putメソッドが最初にtable [hash]を調べ、それが空でなく、そこにあるものがキーを取得していない場合、putメソッドに入力されている場合、それはtable[に移動します。 (ハッシュ+ 1)%TABLE_SIZE]。ただし、同じキーの場合は、値が上書きされるだけです。それで、それは正しく理解されていますか?また、getメソッドとputメソッドは、配列内の場所を検索するのと同じメソッドを使用するため、同じキーを指定すると、配列内の同じ場所に配置されることになりますか?

これらの質問は少し基本的なものかもしれませんが、私はこれを整理するためにかなりの時間を費やしてきました。

編集

そこで、キーと対応する値を使用してノードを構築するだけのNodeクラスを介してHashMapを自分で実装しようとしました。また、getHashCodeメソッドもあり、2つの値を互いに連結します。

また、バケットとして使用するSinglyLinkedList(前の割り当ての一部)を作成しました。

そして、私のハッシュ関数は単純にhashCode%hashMap.lengthです。

これが私自身の実装です、それであなたはそれについてどう思いますか?

package spreadsheet; 

public class HashTableMap {

  private SinglyLinkedListMap[] hashArray;
  private int size;


  public HashTableMap() {
    hashArray = new SinglyLinkedListMap[64];
    size = 0;  
  }


  public void insert(final Position key, final Expression value) {

      Node node = new Node(key, value); 
      int hashNumber = node.getHashCode() % hashArray.length;       
      SinglyLinkedListMap bucket = new SinglyLinkedListMap();
      bucket.insert(key, value);
      if(hashArray[hashNumber] == null) {
        hashArray[hashNumber] = bucket;
        size++; 
      }
      if(hashArray[hashNumber] != null) {
        SinglyLinkedListMap bucket2 = hashArray[hashNumber];
        bucket2.insert(key, value);
        hashArray[hashNumber] = bucket2;
        size++; 
      }
      if (hashArray.length == size) {
          SinglyLinkedListMap[] newhashArray = new SinglyLinkedListMap[size * 2];
      for (int i = 0; i < size; i++) {
          newhashArray[i] = hashArray[i];
      }
      hashArray = newhashArray;
    }
  } 

  public Expression lookUp(final Position key) {
      Node node = new Node(key, null); 
      int hashNumber = node.getHashCode() % hashArray.length;
      SinglyLinkedListMap foundBucket = hashArray[hashNumber];
      return foundBucket.lookUp(key); 
  }
 }

ルックアップ時間はO(1)前後になるはずですが、そうなのか知りたいのですが。そうでなければ、その点でどうすればそれを改善できますか?

4

4 に答える 4

9

2つの異なるキーが同じバケット、つまり配列の同じ要素に含まれるハッシュ衝突に対処するための計画を立てる必要があります。

最も簡単な解決策の1つは、各バケットのエントリのリストを保持することです。

優れたハッシュアルゴリズムがあり、バケットの数が要素の数よりも大きいことを確認すると、ほとんどのバケットに0個または1個のアイテムが含まれるようになるため、リスト検索に時間がかかることはありません。リストが長くなりすぎている場合は、データを分散させるために、より多くのバケットで再ハッシュする必要があります。

于 2013-01-28T18:30:08.180 に答える
1

それは本当にあなたのハッシュコードメソッドがどれだけ優れているかに依存します。あなたがそれをできるだけ悪くしようとしたとしましょう:あなたは毎回ハッシュコードが1を返すようにしました。その場合、リストの配列がありますが、配列の1つの要素だけにデータが含まれます。その要素は、その中に巨大なリストを持つように成長するでしょう。

そうすると、非常に非効率的なハッシュマップになります。ただし、ハッシュコードが少し優れていれば、オブジェクトを多くの異なる配列要素に分散し、その結果、はるかに効率的になります。

最も理想的なケース(多くの場合、達成できない)は、どのオブジェクトを入れても一意の番号を返すハッシュコードメソッドを使用することです。それができれば、リストの配列は必要ありません。配列を使用することもできます。ただし、ハッシュコードは「完全」ではないため、2つの異なるオブジェクトが同じハッシュコードを持つ可能性があります。同じ配列要素のリストにそれらを配置することにより、そのシナリオを処理できる必要があります。

ただし、ハッシュコードメソッドが「かなり良い」ものであり、衝突がほとんど発生しない場合は、リストに複数の要素が含まれることはめったにありません。

于 2013-01-28T18:30:51.320 に答える
0

これらListsはバケットと呼ばれることが多く、衝突に対処する方法です。2つのデータ要素が同じハッシュコードmodTABLESIZEを持つ場合、それらは衝突しますが、両方を格納する必要があります。

より悪い種類の衝突は、同じものを持つ2つの異なるデータポイントkeyです。これはハッシュテーブルでは許可されておらず、一方が他方を上書きします。行を列に追加するだけの場合、(2,1)と(1,2)は両方とも3のキーを持ちます。これは、同じハッシュテーブルに格納できないことを意味します。区切り文字なしで文字列を連結した場合、問題は(12,1)と(1、21)にあります---両方にキー "121"があります区切り文字(コンマなど)を使用すると、すべてのキーが区別されます。

ハッシュコードが同じmodTABLE_SIZEである場合、異なるキーは同じ金額に収まる可能性があります。これらのリストは、2つの値を同じバケットに格納する1つの方法です。

于 2013-01-28T18:30:31.273 に答える
0
class SpreadSheetPosition {
    int column;
    int row;

    @Override
    public int hashCode() {
        return column + row;
    }
}

class HashMap {
    private Liat[] buckets = new List[N];

    public void put(Object key, Object value) {
        int keyHashCode = key.hashCode();
        int bucketIndex = keyHashCode % N;
        ...
    }
}

N 個のリストがある場合と、リスト/配列が 1 つしかない場合を比較してください。リストを検索するには、おそらくリスト全体をトラバースする必要があります。リストの配列を使用することで、少なくとも 1 つのリストが削減されます。1 つまたは 0 の要素 (null) のリストを取得する可能性さえあります。

hashCode()が可能な限りユニークである場合、すぐに見つかる可能性が高くなります。

于 2013-01-28T18:42:40.867 に答える