提问人:rickygrimes 提问时间:8/3/2013 最后编辑:rickygrimes 更新时间:3/20/2014 访问量:27973
字符串作为哈希图 [duplicate] 中的键
String as key in hashmap [duplicate]
问:
在过去的一个小时内,我阅读了很多帖子,但我仍然不太清楚在 Hashmap 中使用不可变对象作为键的概念。我有一个哈希图,它的键是字符串。哈希图中的值是 MyStore,其中 MyStore 表示有关我拥有的商店的信息。String 表示地址。在我的代码中,我的逻辑是,我首先在映射中查找该键,如果存在 --> 获取它的值,如果它不存在,请将其放入 hashmap。我的经理刚刚告诉我,钥匙将来会改变,也就是说,我的商店地址将来会改变。他说,在那种情况下,我首先检查密钥是否存在的逻辑是行不通的。我不明白他在这里是什么意思。我想非常清楚地了解以下几点——
- 哈希图的可变键和不可变键之间的区别。
- 如果使用可以更改的不可变密钥,会发生什么情况?- 我知道这没有意义,但我想清楚地了解我的经理在这里说什么。
- 一些帖子谈到字符串,如果用作哈希映射中的键,缓存它们的哈希码 - 这意味着什么?
- 如果假设我在哈希图中使用可变对象作为键,实现了哈希码和等号,那么它会起作用吗?我假设它会,因为如果键更改,contains 方法将查看键是否存在。如果它不存在,它将放置该条目,以便您将来可以获取它。
如果之前已经讨论过,我并不是要创建重复的帖子。如果我错过了阅读包含我所有问题答案的帖子,请指出我。如果没有,请用通俗易懂的语言解释我的上述问题,以便将来对其他读者:)有用。随意编辑我帖子的主题,以便将来如果有人有类似的问题,他们会直接登陆这里:)
答:
一般来说,哈希图中的键应该是不可变的。
看到这个
注意:如果将可变对象用作映射,则必须格外小心 钥匙。如果对象的值 以影响等于比较的方式进行更改,而 对象是映射中的键。
密钥的哈希值在插入期间计算一次,哈希图存储它,一旦您的密钥被修改,它就不会自动更新。这就是为什么有一个假设,即密钥是不可变的。
您的选择包括: 1. 不要使用可变对象作为键。尝试查找另一个键,或使用前一个键对象的不可变部分 2. 在可变对象用作键时,不要更改它们
评论
如果修改用作键的可变对象,则可能会出现问题。 即使密钥存在,也可能会返回,您必须遍历密钥才能找到它。 因此,请尝试使用 immutable,或者不要在它是键时修改 mutable。
map.containsKey(modifiedKey)
false
不可变对象永远不会改变。有些方法看起来像是在更改对象,但实际上会创建一个新副本。例:
字符串 a = “A”;
字符串 b = a.substring(0);子字符串创建了一个“A”的副本,其中 a 根本没有被修改。
a = a + b;a+b 创建一个新的字符串“AA”,而不修改以前的字符串。
这可能有助于在java集合中缓存哈希,这也很好,为什么在哈希图中如此有效
String 已经实现了 和 ,除非您绝对确定需要它,否则无需发明另一个类来代替它。
equals
hashcode
如第 1 点所述。你可以这样做,但你必须小心,不要修改你的可变对象。不过,这不是一个很好的做法。
评论
不可变键无法更改。因此,在插入时计算的哈希码不能更改。因此,当您尝试从映射中获取元素时,将根据已知的哈希码计算要获取的对象的哈希码。如果您的密钥从外部更改(它是可变的),则新密钥的哈希码将与您插入的哈希码不同。
让我们看一个例子。for( 和
2
4
)public class RandomPair { int p; int q; public RandomPair(int p, int q) { this.p = p; this.q = q; } @Override public int hashCode() { return 31 * p + q; } @Override public boolean equals(Object obj) { if (!(obj instanceof RandomPair)) { return false; } if (obj == this) { return true; } RandomPair other = (RandomPair) obj; if (p != other.p) return false; if (q != other.q) return false; return true; } public static void main(String[] args) { RandomPair pair = new RandomPair(10, 10); Map<RandomPair, Integer> map = new HashMap<RandomPair, Integer>(); map.put(pair, 1); System.out.println(map.get(pair)); //returns 1 //someone somewhere just changed the value of pair pair.p = 20; //the object was the same, someone somewhere just changed value of pair and now you can't //find it in the map System.out.println(map.get(pair)); //had you made p and q final, this sort of modification wouldn't be possible //Strings are immutable and thus prevent this modification } }
由于字符串是不可变的,因此计算后的哈希码值可以再次重用。这 哈希码是延迟计算的。即在第一次调用 HashCode 时,然后缓存 HashCode 的值。
评论
hashcode
equals
hashcode
Person
第一:HashMap是如何工作的?
基本上它有一个数组,当你在地图中放置一个键值对时,它被存储在数组中的一个位置。数组中的位置是根据传递给哈希方法的密钥的结果来选择的。为什么?好吧,如果您请求某个键的值,则可以简单地重新计算数组中用于查找键及其关联值的索引以再次查找数组中的索引。(需要更多的逻辑来处理映射到同一索引的键,但我只是想让你了解基本机制)然后用于验证计算索引处的键是否确实是请求的键。hashCode()
equals()
由此应该更清楚为什么不可变键比可变键更好。不可变键将始终保持相同的值,哈希函数将再次找到正确的存储桶( = hashMap 数组中的索引)。
hashCode()
这并不意味着可变键不能工作。如果键上的突变不影响哈希码,或者只要使用 hashMap,键就不会发生突变,则可变键将起作用。
不可变的密钥如何更改?好吧,键本身可能无法更改,但键值映射可以在业务逻辑中更改。如果您使用地址作为键创建地图,则依赖于商店地址不会更改的事实。如果商店的地址发生更改,您将无法在地图中找到该商店,使用其新地址作为键。你的经理说得有道理。
在 Map 中查找密钥的速度很大程度上取决于计算 hashCode 的速度。对于 String,此计算将循环 String 中的所有字符。如果使用长字符串作为键,并且具有大量映射访问权限,则可能会导致性能瓶颈。因此,Java 的 String 实现会缓存哈希值,因此它只会计算一次。但是,只有在再次使用同一实例时,才能避免计算哈希代码(新实例将没有缓存值)。您可以使用密钥,但只有在可以证明确实存在性能瓶颈时才考虑这一点,因为实习确实有其自身的开销。
String
intern()
String
如 1 中所述:如果可变密钥的哈希码不受突变的影响,则可变密钥可以工作。例如,使用 Customer 作为键,其中仅基于客户的名称,那么 Customer 实现不仅不允许更改名称,但允许其他值更改,则是一个可靠的键。
hashCode()
评论
hashCode()
hashCode()
可变键或对象意味着您可以修改对象 [通过修改,我的意思是您可以更改对象表示的值]。如果用 equals 编写的逻辑和哈希码使用这些可修改值,这将影响其存储。
HashMap
理想情况下,不可变性意味着对象一旦初始化就不能在此之后更改。但是,如果我们具体地谈论在等于和哈希码方法中使用的所有变量,如果它们可以被修改,那么该对象不应该用作键,否则它可以用作键[但仍然不推荐]。
HashMap
它不仅仅是关于,任何关于都会缓存它的哈希码。哈希码几乎是为几乎所有对象一次又一次地生成的[我说几乎是有原因的,因为在某些情况下它可以改变]。哈希码缓存在 Object 标头中。
String
如果你想使用可变对象作为键,那么你应该去 .只需阅读它们,它们在这种情况下可能很有用。
IdentityHashMap
评论