关于在Java中实现我自己的HashMap的问题

时间:2013-01-28 18:26:41

标签: java hashmap

我正在进行一项任务,我必须实现自己的HashMap。在赋值文本中,它被描述为一个列表数组,每当你想要添加一个元素时,它最终在数组中的位置由其hashCode决定。在我的例子中,它是电子表格中的位置,所以我刚刚使用了columnNumber + rowNumber,然后将其转换为String,然后转换为int,作为hashCode,然后我将其插入到Array中。它当然以节点(键,值)的形式插入,其中键是单元格的位置,值是单元格的值。

但我必须说我不明白为什么我们需要一个列表数组,因为如果我们最终得到一个包含多个元素的列表,它会不会相当大地增加查找时间?那么它不应该是一个节点数组吗?

我也发现了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方法首先在表[hash]中查找是正确的,如果那不是空的,如果那里没有得到密钥,则在put方法中输入,然后继续to table [(hash + 1)%TABLE_SIZE]。但如果它是相同的键,它只是覆盖该值。这是正确理解的吗?是因为get和put方法使用相同的方法查找数组中的位置,给定相同的键它们最终会在数组中的同一位置?

我知道这些问题可能有些基本,但我花了很多时间试图解决这个问题,为什么我们会非常感谢任何帮助!

修改

所以现在我尝试通过Node类自己实现HashMap 构造一个带有键和相应值的节点,它还有一个getHashCode方法,我只是将这两个值相互连接起来。

我还构建了一个SinglyLinkedList(前一个作业的一部分),我将其用作存储桶。

我的Hash函数只是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 个答案:

答案 0 :(得分:9)

你必须有一些计划来处理哈希冲突,其中两个不同的键落在同一个桶中,即阵列的相同元素。

最简单的解决方案之一是保留每个存储桶的条目列表。

如果你有一个好的哈希算法,并确保桶的数量大于元素的数量,你应该最终得到大多数桶有零个或一个项目,所以列表搜索不应该花费很长时间。如果列表变得太长,则需要重新使用更多存储桶来传播数据。

答案 1 :(得分:1)

这实际上取决于你的hashcode方法有多好。让我们说你试图让它变得尽可能糟糕:你每次都使哈希码返回1。如果是这种情况,您将拥有一个列表数组,但只有该数组的一个元素将包含任何数据。该元素只会在其中有一个巨大的列表。

如果你这样做,你的hashmap会非常低效。但是,如果您的哈希码稍好一些,它会将对象分配到许多不同的数组元素中,因此效率会更高。

最理想的情况(通常无法实现)是使用哈希码方法,无论您放入什么对象,都会返回唯一的数字。如果你能做到这一点,你就不需要一系列的列表。你可以使用一个数组。但由于您的哈希码不是“完美”,因此两个不同的对象可能具有相同的哈希码。您需要能够通过将它们放在同一个数组元素的列表中来处理该场景。

但是,如果您的哈希码方法“非常好”并且很少发生冲突,那么列表中很少会有多于1个元素。

答案 2 :(得分:0)

Lists通常被称为桶,是处理冲突的一种方式。当两个数据元素具有相同的哈希码mod TABLE SIZE时,它们会发生碰撞,但两者都必须存储。

更糟糕的一种冲突是两个不同的数据点具有相同的key - 这在哈希表中是不允许的,一个将覆盖其他数据点。如果只是将行添加到列,则(2,1)和(1,2)都将具有3的键,这意味着它们不能存储在同一个哈希表中。如果你在没有分隔符的情况下将字符串连接在一起,则问题在于(12,1)与(1,21)---两者都有键“121”对于分隔符(例如逗号),所有键都是不同的。

如果hashcode是相同的mod TABLE_SIZE,则不同的密钥可以位于同一个buck中。这些列表是将两个值存储在同一个存储桶中的一种方法。

答案 3 :(得分: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个列表,只有一个列表/数组。为了在列表中搜索,可能必须遍历整个列表。通过使用列表数组,至少可以减少单个列表。甚至可能获得一个或零个元素的列表(null)。

如果hashCode()尽可能独特,立即找到的机会很高。