2017-03-09 55 views
1
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迪帕克长度6爪哇 - 哈希映射retreival序列

的密钥哈希码113哈希码>> 112键:P

哈希码96的密钥哈希码>> 97 key:a

hashcode密钥的哈希码>> 100 key:d

哈希码103哈希码密钥>> 101键:电子

哈希码106哈希码的密钥>> 107关键字:k

>>> {p = 1时,A = 1,d = 1,E = 2,K = 1}

谁能帮我了解从程序和输出两件事情:通过地图对象印刷

  1. 的数据,如何决定在内部序列? 例如。它是打印序列p,a,d,e,k。

  2. entry.hashcode()和entry.key()。hashcode()有什么区别? 请参考输出解释差异。

+0

您的条目的内部顺序不受HashMap的保证。他们可以在任何其他地方。看看[这里](http://stackoverflow.com/questions/683518/java-class-that-implements-map-and-keeps-insertion-order)。 – GAlexMES

+0

区别在[Java API](https://docs.oracle.com/javase/7/docs/api/java/util/Map.Entry.html#hashCode()) – GAlexMES

+0

中有说明程序开始处的'System.gc()'? – Holger

回答

0
  1. 据我所知,一个HashMap不garantueing的Entrys或键的顺序。如果你想让它们排序,你将需要一个TreeMap或者链接地图。 See here.。 HashMap根本没有顺序。

  2. 我已经评论你链接到Java API,这说明它很好。但我会解释reffering到您的输出:

哈希码113哈希码键>> 112键:P

这意味着:入口的hashCode是113你的关键的哈希码是112.你不打印你的Entry的HashCode,它是1.条目的HashCode是用Key的HashCode和Value的HashCode计算的。那些HashCodes将被着色。

+0

我同意没有HashMap保证的顺序,但我已经多次尝试多个字符串数据,但它总是以相同的顺序响应各个String数据,所以它意味着它有一些内部模式来打印输出。 –

+0

不,它没有。如果你在不同的机器或不同的操作系统,不同的处理器架构上尝试相同的东西,或者只是经常尝试。然后会有不同的顺序。 – GAlexMES

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()对于任何两个条目e1e2,由总合同Object.hashCode

这符合Map意见的预期行为。如果你有两个Map S,ab,然后当且仅当这两个地图都有相同的键

  • a.keySet().equals(b.keySet())将是真实的。
  • a.entrySet().equals(b.entrySet())将为真当且仅当两者的地图具有相同的键每个键映射到用于每个映射为相同的值,换言之,这两个图是相等的。

    这甚至specified in Map.equals

    返回true如果给定对象也是一个映射并且两个映射表示相同的映射。更正式地说,如果m1.entrySet().equals(m2.entrySet()),则两张图m1m2表示相同的映射。

  • 由于键和条目是不同的东西,a.keySet().equals(a.entrySet())只能是真实的,如果a是空的。

+0

当然!为什么不在这么多标签上评论最不尊敬的用户呢?这是一个很好的(和详细的!)答案...一加 – Eugene

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

这里的其他答案都指出,Map的说明也是这样说的。

这甚至更好的是出现在JDK-9一成不变的地图:

Map<String, Integer> map2 = Map.of("1", 1, "2", 2, "3", 3); 
System.out.println(map2); 

这个map2的输出将从每次运行变化!这对于像这样的输出(在不同的虚拟机上运行的两倍)完全合法:

{1=1, 3=3, 2=2} 

{1=1, 2=2, 3=3} 

这正是因为地图没有订单,所以他们实施的地图内随机图案。

编辑

的解释一点点。

我会尝试,但这是一个整体的大课题。所以一个HashMap将使用buckets来存储它的键/值对。根据大小,桶实际上是一个Node s(红/黑TreeNode或LinkedNode)的数组。条目使用键和模的hashCode转到某个存储桶。那么不是直接模块,而是使用& operator(两个散列与模数的搜索能力)的技巧。重新调整大小(实际上是内部数组上的加倍 - 因此触发重新哈希)是在某些条件下完成的,其中最显着的是当达到load_factor时(但不是唯一的)。因此,如果声明:new HashMap(16),则加载因子为0.75,因此当您添加13条目时,内部数组将重新调整为32个条目。这也会触发密钥的重新哈希(在引擎盖下还有一点是在选择桶时考虑到的 - 所以可能会移动 - 就像我提供的示例中那样)。这不依赖于JVM,但依赖于实现。 这是jdk-8和jdk-9当时的行为。但无论哪种方式你不应该在乎,而是依赖于地图提供的一般合同。

+0

感谢您详细解释。我知道在HashMap调整大小发生75%,但我不知道它是如何实际工作的某些特定的程序或机器。你能解释一下hashmap在内存中调整大小的方式以及它实际上触发调整大小的方法吗?调整大小对于程序来说是特定的,还是依赖于JVM? –

+0

@SomeshGupta我做了一个编辑,添加*一些*细节。但通常这是一个需要分离的重大课题;一次不容易理解。我试图解决这个问题,前一段时间,这里是我发布我的发现的地方.. https://www.slideshare.net/secret/8k8EP71cNydeib – Eugene

+0

谢谢@Eugene。但是当你说这一行时 - “重新调整大小(实际上是内部数组的双倍 - 因此触发重新哈希)是在某些条件下完成的”,所以你的意思是内部数组List(Map.Entry),或者你在说话关于别的东西? –