import java.util.HashMap;
import java.util.Map.Entry;
public class TestString {
public static void main(String[] args) {
System.gc();
String str = "deepak";
int length = str.length();
System.out.println("str " + str + " length " + length);
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
for(int i=0; i<length; i++){
char ch = str.charAt(i);
if(map.containsKey(ch)){
Integer val = map.get(ch);
map.put(ch, val+1);
}else{
map.put(ch, 1);
}
}
for (Entry<Character, Integer> entry : map.entrySet())
{
int hashCode = entry.hashCode();
char key = entry.getKey();
// int hash = hash();
System.out.println("hashcode " + hashCode + " hashcode of key>> " + entry.getKey().hashCode() + " key : " + key);
}
System.out.println(">>> " + map);
}
}
输出:
str deepak长度6哈希码113键的哈希码&gt;&gt; 112键:p
哈希码96键的哈希码&gt;&gt; 97键:a
哈希码101键的哈希码&gt;&gt; 100键:d
哈希码103键的哈希码&gt;&gt; 101键:e
哈希码106密钥的哈希码&gt;&gt; 107键:k
>>>
{p = 1,a = 1,d = 1,e = 2,k = 1}
任何人都可以帮助我理解程序和输出中的两件事:
地图对象打印的数据,如何在内部决定序列? 例如。它是打印序列p,a,d,e,k。
entry.hashcode()和entry.key()。hashcode()有什么区别? 请参考输出来解释差异。
答案 0 :(得分:0)
答案 1 :(得分:0)
迭代顺序未指定,从技术上讲,它是关键哈希码,实际地图实现,地图容量以及某些情况下特定地图实例的历史记录的函数。
您可以通过将代码更改为
来轻松显示对地图容量的依赖性String str = "deepak";
int length = str.length();
System.out.println("str " + str + " length " + length);
HashMap<Character,Integer> map = new HashMap<>(100);
for(int i=0; i<length; i++) {
char ch = str.charAt(i);
map.merge(ch, 1, Integer::sum);
}
for(Entry<Character, Integer> entry: map.entrySet()) {
int hashCode = entry.hashCode();
Character key = entry.getKey();
System.out.printf("hashcode %3d, hashcode of key %3d, key : %s%n",
hashCode, key.hashCode(), key);
}
map = new HashMap<>(map);
System.out.println(">>> " + map);
在我的环境中,打印
str deepak length 6
hashcode 96, hashcode of key 97, key : a
hashcode 101, hashcode of key 100, key : d
hashcode 103, hashcode of key 101, key : e
hashcode 106, hashcode of key 107, key : k
hashcode 113, hashcode of key 112, key : p
>>> {p=1, a=1, k=1, d=1, e=2}
显示具有不同容量的地图如何展示不同的迭代次序。
密钥和Map.Entry
实例是不同的对象,因此,它们具有不同的哈希码并不奇怪。 Map.Entry
的哈希码为well specified
映射条目e的哈希码定义为:
(e.getKey()==null ? 0 : e.getKey().hashCode()) ^ (e.getValue()==null ? 0 : e.getValue().hashCode())
根据
e1.equals(e2)
的一般合同的要求,这可确保e1.hashCode()==e2.hashCode()
隐含任意两个条目e1
和e2
的{{1}}。
这符合Object.hashCode
视图的预期行为。如果您有两个Map
,Map
和a
,那么
b
为真。 a.keySet().equals(b.keySet())
如果两个地图具有相同的键和,则每个地图都映射到相同的值,换句话说,两个地图都相等。< / p>
如果给定对象也是一个映射,并且两个映射表示相同的映射,则返回
a.entrySet().equals(b.entrySet())
。更正式地说,如果true
,则两个地图m1
和m2
代表相同的映射。
由于键和条目不同,m1.entrySet().equals(m2.entrySet())
只有a.keySet().equals(a.entrySet())
为空时才会为真。
答案 2 :(得分:0)
有点不清楚你在这里要证明什么或者问,但是一个更简单的例子怎么样:
HashMap<String, Integer> map = new HashMap<>();
map.put("HZV", 1);
map.put("CJX", 2);
System.out.println(map); // {CJX=2, HZV=1}
for (int i = 0; i < 16; ++i) {
map.put("" + i, i);
}
for (int i = 0; i < 16; ++i) {
map.remove("" + i);
}
System.out.println(map); // {HZV=1, CJX=2}
注意上面示例中的两个打印件如何存储相同的内容,地图中的条目是相同的;唯一的区别是它们的顺序不同。
这是因为内部地图存储中的buckets
数量增加了,因为条目为re-hashed
,并且它们移动到其他存储桶。从地图检索条目时,显而易见there is no guaranteed sequence
。
这里的其他答案,已经指出,地图的规范也说明了。
在jdk-9不可变地图中可以看到更好:
Map<String, Integer> map2 = Map.of("1", 1, "2", 2, "3", 3);
System.out.println(map2);
此map2
的输出因运行而异!这样的输出完全合法(在不同的VM中运行两次):
{1=1, 3=3, 2=2}
{1=1, 2=2, 3=3}
这正是因为Map没有订单,所以他们在Map中实现了随机化模式。
修改强>
一点点解释。
我会尝试,但这是一个很重要的主题。因此,HashMap将使用buckets
来存储它的键/值对。存储桶实际上是Node
s(红色/黑色TreeNode或LinkedNode)的数组,具体取决于大小。条目使用密钥和模数的hashCode转到某个存储桶。好吧不是直接模块,而是使用& operator
的技巧(搜索两个哈希vs modulo的力量)。重新调整大小(实际上是内部数组加倍 - 从而触发重新哈希)是在某些条件下完成的,大多数明显的是在达到load_factor时(但这不是唯一的) )。因此,如果您声明:new HashMap(16)
,则加载因子为0.75,因此当您添加13条目时,内部数组将重新调整大小为32个条目。这也将触发密钥的重新哈希(在引擎盖下还有一个在选择存储桶时要考虑的位 - 因此entires可能会移动 - 就像我提供的示例中一样)。这不依赖于JVM,而是依赖于实现。
这是jdk-8和jdk-9中此刻的行为。但无论如何你不应该关心,而是依赖于Map提供的一般合同。