【问题标题】:Problems with my implementation of a Java Hashmap我实现 Java Hashmap 的问题
【发布时间】:2014-07-18 10:38:30
【问题描述】:

在研究并查找旧帖子一段时间后,我意识到当您在 Java 中使用字符串为键的 Hashmap 或 Hashtable 时,第一“轮”散列将应用于每个 String 对象 hashCode(显然有第二个哈希函数应用于int hashCode()) 的结果,默认情况下int hashCode()与其调用者在内存中的位置有某种关系(从我读到的)。话虽如此,如果我有一个带有开发人员定义的键类的映射,我读到我可以覆盖 int hashCode() 并使用我的对象的一些不同字段来为每个对象返回最独特的 int 可能。但是,请考虑下面包含原始类型数组的代码片段。

import java.util.HashMap;

public class test
{ 

    public static void main(String[] args) { 

        HashMap<char[], int[] > map = new HashMap<char[], int[]>();

        String s = "Hello, World";

        int x[] = { 1, 2, 3, 4, 5 };

        map.put( s.toCharArray(), x );

        x = map.get( s );

        for ( int i : x )
            System.out.print( i );
    } 
}

程序从NullPointerException 崩溃当然是因为map.get( s ); 返回null。我怀疑这是因为map.put()map.get() 之间有两个不同的引用。我希望程序输出的是 1 2 3 4 5。

我的问题:如何让上面的代码片段通过键的值与键的引用来查找键?也就是如何让程序输出1 2 3 4 5?

编辑: 我使用哈希图作为查找表。我正在从文件中读取字符串,需要一种快速的方法来确定我刚刚读入的字符串是否在表中。

【问题讨论】:

    标签: java string-hashing


    【解决方案1】:

    只需使用字符串作为地图的键。

    HashMap<String, int[] > map = new HashMap<String, int[]>();
    String key = "array1";
    int x[] = { 1, 2, 3, 4, 5 };
    map.put( key, x );
    

    String 是不可变的,所以它作为映射的键是不错的选择。

    添加另一个数组:

    String key2 = "array2";
    int x2[] = { 6, 7, 8, 9, 10 };
    map.put( key2, x2 );
    

    输出值:

    x = map.get( key );
    for ( int i : x )
        System.out.print( i + " " );
    }
    

    给予

    1 2 3 4 5

    x = map.get( key2 );
    for ( int i : x )
        System.out.print( i + " " );
    }
    

    给予

    6 7 8 9 10

    【讨论】:

      【解决方案2】:
      1. “在某处读到有 2 轮散列”。不。如果您想查看字符串是如何散列的 - 请查看代码。
      2. 哈希映射的基本约定是,如果键具有相同的哈希并且通过它们的equals 函数相等,它将检索项目。为什么你认为 char[] 有一个被覆盖的 equals 允许它正确地与字符串进行比较?甚至彼此之间?它确实覆盖equals,并且只有当它是同一个实例时才会返回true。
      3. 您正在使用数组作为键。这是可能的,但大多数高级开发人员会为此大喊大叫。没有很好的方法来比较它们:Object.equals(它们默认使用)意味着您无法复制数组 - 您必须使用 完全相同的对象。使用 Arrays.equals(或类似的方法来比较内容)将意味着您将可变对象作为键 - BAD

      根据您的评论,我了解您希望能够编辑字符串。在您将其放入地图之前,这很好。但是不要更改对象,因为它们是映射中的键,可能会更改哈希码或等于。最好的方法是使用自定义对象。数组不适合这个。

      如果您真的想要在地图中即时更改值 - 使用 BiMap(或自己在 forcePut 中执行的操作)。

      【讨论】:

      • 看看接受的答案。这是我读到有第二个哈希函数的地方。也许我误解了什么? stackoverflow.com/questions/9364134/…
      • @user3270407 但这对于 any 对象是正确的,而不仅仅是字符串。仅仅因为hashCode 的输出是任意的int,而HashMap 中的槽数小于整数数。第二步是特定于地图,并且会发生任何插入的对象。事实上,在许多幼稚的地图中,它是一个简单的mod
      【解决方案3】:

      有很多方法可以做到这一点。您可以做的最小更改之一是在调用put() 之前简单地保存您的char[],并使用与get() 相同的参数:

      char[] charArray = s.toCharArray();
      map.put(charArray, x);
      x = map.get(charArray);
      

      这里重要的是,您需要对get() 使用与put() 相同的对象。

      【讨论】:

        【解决方案4】:

        来自 Oracle 文档

        public V get(Object key)
        
        Returns the value to which the specified key is mapped, or null if this 
        map contains no mapping for the key.
        More formally, if this map contains a mapping from a key k to a value v 
        such that (key==null ? k==null : key.equals(k)), then this method returns v; 
        otherwise it returns null.
        

        显然 s.equals(s.toCharArray()) 是错误的。他们甚至不是同一个班级。

        但是,您可以将覆盖 equals() 的 Class 用作键,以针对您的情况返回 true。 例如:

        class MyCharArray {
        
           private char[] data;
        
           @Override
           public boolean equals(Object o) {
               if (o instanceOf String) {
                   return data.equals(o.toCharArray);
               else {
                   return false;
               }
           }
        
           ...
        }
        

        【讨论】:

        • 虽然 this equals 确实会返回 false,但它不是对象需要是“相同的对象”甚至“相同的类”的必要前提条件返回真。
        猜你喜欢
        • 2012-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-10
        • 2012-07-25
        • 1970-01-01
        相关资源
        最近更新 更多