GVKun编程网logo

HashMap、HashTable、LinkedHashMap和TreeMap用法和区别(hashmap,linkedmap,treemap的区别)

10

如果您想了解HashMap、HashTable、LinkedHashMap和TreeMap用法和区别和hashmap,linkedmap,treemap的区别的知识,那么本篇文章将是您的不二之选。我们

如果您想了解HashMap、HashTable、LinkedHashMap和TreeMap用法和区别hashmap,linkedmap,treemap的区别的知识,那么本篇文章将是您的不二之选。我们将深入剖析HashMap、HashTable、LinkedHashMap和TreeMap用法和区别的各个方面,并为您解答hashmap,linkedmap,treemap的区别的疑在这篇文章中,我们将为您介绍HashMap、HashTable、LinkedHashMap和TreeMap用法和区别的相关知识,同时也会详细的解释hashmap,linkedmap,treemap的区别的运用方法,并给出实际的案例分析,希望能帮助到您!

本文目录一览:

HashMap、HashTable、LinkedHashMap和TreeMap用法和区别(hashmap,linkedmap,treemap的区别)

HashMap、HashTable、LinkedHashMap和TreeMap用法和区别(hashmap,linkedmap,treemap的区别)

Java为数据结构中的映射定义了一个接口java.util.Map,它有四个实现类,分别是HashMap、HashTable、LinkedHashMap和TreeMap。本节实例主要介绍这4中实例的用法和区别。
关键技术剖析:
Map用于存储键值对,根据键得到值,因此不允许键重复,值可以重复。
l  (1)HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为null,不允许多条记录的值为null。HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要同步,可以用Collections.synchronizedMap(HashMap map)方法使HashMap具有同步的能力。
l  (2)Hashtable与HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,然而,这也导致了Hashtable在写入时会比较慢。
l  (3)LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的。在遍历的时候会比HashMap慢。有HashMap的全部特性。
l  (4)TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。当用Iteraor遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
 
 
public class TestMap {
  
  
   public static void init(Map map){
      if (map != null){
         String key = null;
         for (int i=5; i>0; i--){
            key = new Integer(i).toString() + ".0";
            map.put(key, key.toString());
            //Map中的键是不重复的,如果插入两个键值一样的记录,
            //那么后插入的记录会覆盖先插入的记录
            map.put(key, key.toString() + "0");         }
      }
   }
  
   public static void output(Map map){
      if (map != null){
         Object key = null;
         Object value = null;
         //使用迭代器遍历Map的键,根据键取值
         Iterator it = map.keySet().iterator();
         while (it.hasNext()){
            key = it.next();
            value = map.get(key);
            System.out.println("key: " + key + "; value: " + value );
         }
         //或者使用迭代器遍历Map的记录Map.Entry
         Map.Entry entry = null;
         it = map.entrySet().iterator();
         while (it.hasNext()){
            //一个Map.Entry代表一条记录
            entry = (Map.Entry)it.next();
            //通过entry可以获得记录的键和值
            //System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
         }
      }
   }
  
   public static boolean containsKey(Map map, Object key){
      if (map != null){
         return map.containsKey(key);
      }
      return false;
   }
  
   public static boolean containsValue(Map map, Object value){
      if (map != null){
         return map.containsValue(value);
      }
      return false;
   }
  
   public static void testHashMap(){
      Map myMap = new HashMap();
      init(myMap);
      //HashMap的键可以为null
      myMap.put(null,"ddd");
      //HashMap的值可以为null
      myMap.put("aaa", null);
      output(myMap);
   }
  
   public static void testHashtable(){
      Map myMap = new Hashtable();
      init(myMap);
      //Hashtable的键不能为null
      //myMap.put(null,"ddd");
      //Hashtable的值不能为null
      //myMap.put("aaa", null);
      output(myMap);
   }
  
   public static void testLinkedHashMap(){
      Map myMap = new LinkedHashMap();
      init(myMap);
      //LinkedHashMap的键可以为null
      myMap.put(null,"ddd");
      myMap.put(null,"aaa");
      //LinkedHashMap的值可以为null
      myMap.put("aaa", null);
      output(myMap);
   }
  
   public static void testTreeMap(){
      Map myMap = new TreeMap();
      init(myMap);
      //TreeMap的键不能为null
      //myMap.put(null,"ddd");
      //TreeMap的值不能为null
      //myMap.put("aaa", null);
      output(myMap);
   }
 
   public static void main(String[] args) {
      System.out.println("采用HashMap");
      TestMap.testHashMap();
      System.out.println("采用Hashtable");
      TestMap.testHashtable();
      System.out.println("采用LinkedHashMap");
      TestMap.testLinkedHashMap();
      System.out.println("采用TreeMap");
      TestMap.testTreeMap();
     
      Map myMap = new HashMap();
      TestMap.init(myMap);
      System.out.println("新初始化一个Map: myMap");
      TestMap.output(myMap);
      //清空Map
      myMap.clear();
      System.out.println("将myMap clear后,myMap空了么?  " + myMap.isEmpty());
      TestMap.output(myMap);
      myMap.put("aaa", "aaaa");
      myMap.put("bbb", "bbbb");
      //判断Map是否包含某键或者某值
      System.out.println("myMap包含键aaa?  "+ TestMap.containsKey(myMap, "aaa"));
      System.out.println("myMap包含值aaaa?  "+ TestMap.containsValue(myMap, "aaaa"));
      //根据键删除Map中的记录
      myMap.remove("aaa");
      System.out.println("删除键aaa后,myMap包含键aaa?  "+ TestMap.containsKey(myMap, "aaa"));
      //获取Map的记录数
      System.out.println("myMap包含的记录数:  " + myMap.size());
   }
}
 
输出结果:
采用HashMap
key: null; value: ddd
key: 3.0; value: 3.00
key: aaa; value: null
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用Hashtable
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 3.0; value: 3.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用LinkedHashMap
key: 5.0; value: 5.00
key: 4.0; value: 4.00
key: 3.0; value: 3.00
key: 2.0; value: 2.00
key: 1.0; value: 1.00
key: null; value: aaa
key: aaa; value: null
采用TreeMap
key: 1.0; value: 1.00
key: 2.0; value: 2.00
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 5.0; value: 5.00
新初始化一个Map: myMap
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
将myMap clear后,myMap空了么?  true
myMap包含键aaa?  true
myMap包含值aaaa?  true
删除键aaa后,myMap包含键aaa?  false
myMap包含的记录数:  1
 
源码分析:
遍历Map有两种方法:
(1)map的keySet()方法获得键的集合,再调用键集合的iterator方法获得键的迭代器,以此迭代地取出Map中的键,用get方法获得键对应的值,便完成了Map的遍历。代码如下所示:
//使用迭代器遍历Map的键,根据键取值
        Iterator it = map.keySet().iterator();
        while (it.hasNext()){
           key = it.next();
           value = map.get(key);
           System.out.println("key: " + key + "; value: " + value );
        }
(2)使用Map的entrySet方法获得Map中记录的集合,每条对象都是一个Map.Entry对象,使用其getKey方法获得记录的键,使用其getValue方法获得记录的值。代码如下所示:
        //或者使用迭代器遍历Map的记录Map.Entry
        Map.Entry entry = null;
        it = map.entrySet().iterator();
        while (it.hasNext()){
           //一个Map.Entry代表一条记录
           entry = (Map.Entry)it.next();
           //通过entry可以获得记录的键和值
           //System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());




















































CurrentHashMap、HashMap、HashTable的区别

CurrentHashMap、HashMap、HashTable的区别

HashTable

  • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
  • 初始size为11,扩容:newsize = olesize*2+1
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 底层数组+链表实现,可以存储null键和null值,线程不安全
  • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
  • 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
  • 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
  • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
  • 计算index方法:index = hash & (tab.length – 1)

HashMap的初始值还要考虑加载因子:

  •  哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
  • 加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
  • 空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:

  • 容量(capacity):hash表中桶的数量
  • 初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量
  • 尺寸(size):当前hash表中记录的数量
  • 负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)

除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。

“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:

  • 较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)
  • 较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销

程序猿可以根据实际情况来调整“负载极限”值。

 

ConcurrentHashMap

  • 底层采用分段的数组+链表实现,线程安全
  • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
  • Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
  • 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
  • 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

 

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。

在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。

Hashtable是线程安全的,它的方法是同步的,可以直接用在多线程环境中。而HashMap则不是线程安全的,在多线程环境中,需要手动实现同步机制。

Hashtable与HashMap另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。

先看一下简单的类图:

  

从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。

ConcurrentHashMap是使用了锁分段技术来保证线程安全的。

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。 

ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

hashmap hashtable LinkedHashMap   TreeMap

hashmap hashtable LinkedHashMap TreeMap

hashmap: 

线程不安全

并发存数据时,resize时可能会死循环。

hashtable

 

性能参数:

初始容量   : 预设值, 避免或减少reSize

负载因子:reSize的时机。  时间与空间的选择。    负载因子小   元素同位置的概率低,查找时间少,但空间利用率差。  负载因子大  元素同位置的概率高,查找时间多,但空间利用率高。

 

关于1.8的优化

对于重复index的元素,还是链表存储,但当链表长度超过8后,改为用红黑树存储。

 

CurrentHashMap 

jdk1.8版本做了优化 采用 node+cas+  实现高性能读写的

 

 

LinkedHashMap  

在HashMap的基础上 为每个元素 增加了两个变量来表明插入数据前后节点。

TreeMap

按照key排序的Map, 默认按照升序。 可重写排序规则。

 

HashMap vs TreeMap vs Hashtable vs LinkedHashMap

HashMap vs TreeMap vs Hashtable vs LinkedHashMap

这篇文章主要介绍了HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

Map是一个重要的数据结构,本篇文章将介绍如何使用不同的Map,如HashMap,TreeMap,HashTable和LinkedHashMap。

Map概览

Java中有四种常见的Map实现,HashMap,TreeMap,HashTable和LinkedHashMap,我们可以使用一句话来描述各个Map,如下:

HashMap:基于散列表实现,是无序的;TreeMap:基于红黑树实现,按Key排序;LinkedHashMap:保存了插入顺序;Hashtable:是同步的,与HashMap类似;HashMap

如果HashMap的Key是自己定义的对象,那么一般需要覆盖equals()和hashCode()方法,且要遵循他们之间的约定。

package simplejava; import java.util.HashMap; import java.util.Map.Entry; class Dog { String color; Dog(String c) { color = c; } public String toString() { return color + " dog"; } } public class Q26 { public static void main(String[] args) { HashMap hashMap = new HashMap(); Dog d1 = new Dog("red"); Dog d2 = new Dog("black"); Dog d3 = new Dog("white"); Dog d4 = new Dog("white"); hashMap.put(d1, 10); hashMap.put(d2, 15); hashMap.put(d3, 5); hashMap.put(d4, 20); //print size System.out.println(hashMap.size()); //loop HashMap for (Entry entry : hashMap.entrySet()) { System.out.println(entry.getKey().toString() + " - " + entry.getValue()); } } }

结果输出:

4

white dog - 5

red dog - 10

white dog - 20

black dog - 15

注意,我们不小心添加了两个"white dogs“,但是HashMap仍然存储它。这是不合理的,现在我们困惑到底有多少条白狗存入了HashMap,5还是20呢。

其实,Dog类应该是这样定义的:

class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(this.color); } public int hashCode() { return color.length(); } public String toString() { return color + " dog"; } }

结果输出:

3

red dog - 10

white dog - 20

black dog - 15

原因是因为HashMap不允许两个相同的元素存入,默认情况下,Object的hashCode()和equals()会被用于判断两个对象是否相同。默认的hashCode()方法对于不同的对象会返回不同的值,而equals()方法只有当两个引用相等,即指向同一个对象的时候才返回true。如果你不是很清楚,可以自己检验下hashCode()和equals()之间的关系。

举个例子,可以检验下HashMap中最常用的方法,如iteration,print等。

TreeMap

TreeMap是按key排序的,让我们先看下如下代码,了解其“按key排序”思想。

package simplejava; import java.util.Map.Entry; import java.util.TreeMap; class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(this.color); } public int hashCode() { return color.length(); } public String toString() { return color + " dog"; } } public class Q26 { public static void main(String[] args) { Dog d1 = new Dog("red"); Dog d2 = new Dog("black"); Dog d3 = new Dog("white"); Dog d4 = new Dog("white"); TreeMap treeMap = new TreeMap(); treeMap.put(d1, 10); treeMap.put(d2, 15); treeMap.put(d3, 5); treeMap.put(d4, 20); for (Entry entry : treeMap.entrySet()) { System.out.println(entry.getKey() + " - " + entry.getValue()); } } }

结果输出:

Exception in thread "main" java.lang.classCastException: simplejava.Dog cannot be cast to java.lang.Comparable

at java.util.TreeMap.compare(TreeMap.java:1188)

at java.util.TreeMap.put(TreeMap.java:531)

at simplejava.Q26.main(Q26.java:34)

由于TreeSet是基于Key排序的,所以作为key的对象需要相互比较,这就是为什么key需要实现Comparable接口。举个例子,你可以使用字符串作为Key,因为String已经实现了Comparable接口。

现在,让我们改变一下Dog,让它可比较,如下:

package simplejava; import java.util.Map.Entry; import java.util.TreeMap; class Dog implements Comparable { String color; int size; Dog(String c, int s) { color = c; size = s; } public String toString() { return color + " dog"; } @Override public int compareto(Dog o) { return o.size - this.size; } } public class Q26 { public static void main(String[] args) { Dog d1 = new Dog("red", 30); Dog d2 = new Dog("black", 20); Dog d3 = new Dog("white", 10); Dog d4 = new Dog("white", 10); TreeMap treeMap = new TreeMap(); treeMap.put(d1, 10); treeMap.put(d2, 15); treeMap.put(d3, 5); treeMap.put(d4, 20); for (Entry entry : treeMap.entrySet()) { System.out.println(entry.getKey() + " - " + entry.getValue()); } } }

结果打印:

red dog - 10

black dog - 15

white dog - 20

在这个例子中,我们是按dog的size来排序的;

如果"Dog d4 = new Dog("white", 10);"被替换成"Dog d4 = new Dog("white",40);",结果将输出如下信息:

white dog - 20

red dog - 10

black dog - 15

white dog - 5

这是因为当前TreeMap使用compareto()去比较key,不同的size意味着不同的dogs。

HashTable

参考java文档:HashMap与HashTable基本类似,除了非同步和允许null外。

LinkedHashMap

LinkedHashMap是HashMap的子类,意味着它继承了HashMap的特性,除此之外,LinkedHashMap还保存了插入顺序。

让我们使用同样的代码,然后将HashMap替换成LinkedHashMap,如下:

package simplejava; import java.util.LinkedHashMap; import java.util.Map.Entry; class Dog { String color; Dog(String c) { color = c; } public boolean equals(Object o) { return ((Dog) o).color.equals(this.color); } public int hashCode() { return color.length(); } public String toString() { return color + " dog"; } } public class Q26 { public static void main(String[] args) { Dog d1 = new Dog("red"); Dog d2 = new Dog("black"); Dog d3 = new Dog("white"); Dog d4 = new Dog("white"); LinkedHashMap linkedHashMap = new LinkedHashMap(); linkedHashMap.put(d1, 10); linkedHashMap.put(d2, 15); linkedHashMap.put(d3, 5); linkedHashMap.put(d4, 20); for (Entry entry : linkedHashMap.entrySet()) { System.out.println(entry.getKey() + " - " + entry.getValue()); } } }

输出结果:

red dog - 10

black dog - 15

white dog - 20

如果我们使用HashMap,结果如下,区别在于没有保存插入顺序:

red dog - 10

white dog - 20

black dog - 15

更多阅读:ArrayList vs. LinkedList vs. Vector

译文链接:http://www.programcreek.com/2013/03/hashmap-vs-treemap-vs-hashtable-vs-linkedhashmap/

总结

以上所述是小编给大家介绍的HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相关知识,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa

Map是最重要的数据结构。这篇文章中,我会带你们看看HashMap, TreeMap, HashTable和LinkedHashMap的区别。

1. Map概览

Java SE中有四种常见的Map实现——HashMap, TreeMap, Hashtable和LinkedHashMap。如果我们使用一句话来分别概括它们的特点,就是:

  • HashMap就是一张hash表,键和值都没有排序。
  • TreeMap以红-黑树结构为基础,键值按顺序排列。
  • LinkedHashMap保存了插入时的顺序。
  • Hashtable是同步的(而HashMap是不同步的)。所以如果在线程安全的环境下应该多使用HashMap,而不是Hashtable,因为Hashtable对同步有额外的开销。
  1. HashMap

如果HashMap的键(key)是自定义的对象,那么需要按规则定义它的equals()和hashCode()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
classDog {
    String color;
 
    Dog(String c) {
        color = c;
    }
    publicString toString(){  
        returncolor +" dog";
    }
}
 
publicclassTestHashMap {
    publicstaticvoidmain(String[] args) {
        HashMap hashMap =newHashMap();
        Dog d1 =newDog("red");
        Dog d2 =newDog("black");
        Dog d3 =newDog("white");
        Dog d4 =newDog("white");
 
        hashMap.put(d1,10);
        hashMap.put(d2,15);
        hashMap.put(d3,5);
        hashMap.put(d4,20);
 
        //print size
        System.out.println(hashMap.size());
 
        //loop HashMap
        for(Entry entry : hashMap.entrySet()) {
            System.out.println(entry.getKey().toString() +" - "+ entry.getValue());
        }
    }
}

输出:

1
2
3
4
5
4
white dog - 5
black dog - 15
red dog - 10
white dog - 20

注意,我们错误的将”white dogs”添加了两次,但是HashMap却接受了两只”white dogs”。这不合理(因为HashMap的键不应该重复),我们会搞不清楚真正有多少白色的狗存在。

Dog类应该定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
classDog {
    String color;
 
    Dog(String c) {
        color = c;
    }
 
    publicbooleanequals(Object o) {
        return((Dog) o).color ==this.color;
    }
 
    publicinthashCode() {
        returncolor.length();
    }
 
    publicString toString(){  
        returncolor +" dog";
    }
}

现在输出结果如下:

1
2
3
4
3
red dog - 10
white dog - 20
black dog - 15

输出结果如上是因为HashMap不允许有两个相等的元素存在。默认情况下(也就是类没有实现hashCode()和equals()方法时),会使用Object类中的这两个方法。Object类中的hashCode()对于不同的对象会返回不同的整数,而只有两个引用指向的同样的对象时equals()才会返回true。如果你不是很了解hashCode()和equals()的规则,可以看看这篇文章。

来看看HashMap最常用的方法,如迭代、打印等。

3. TreeMap

TreeMap的键按顺序排列。让我们先看个例子看看什么叫作“键按顺序排列”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
classDog {
    String color;
 
    Dog(String c) {
        color = c;
    }
    publicbooleanequals(Object o) {
        return((Dog) o).color ==this.color;
    }
 
    publicinthashCode() {
        returncolor.length();
    }
    publicString toString(){  
        returncolor +" dog";
    }
}
 
publicclassTestTreeMap {
    publicstaticvoidmain(String[] args) {
        Dog d1 =newDog("red");
        Dog d2 =newDog("black");
        Dog d3 =newDog("white");
        Dog d4 =newDog("white");
 
        TreeMap treeMap =newTreeMap();
        treeMap.put(d1,10);
        treeMap.put(d2,15);
        treeMap.put(d3,5);
        treeMap.put(d4,20);
 
        for(Entry entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() +" - "+ entry.getValue());
        }
    }
}

输出:

1
2
3
Exception in thread "main" java.lang.ClassCastException: collection.Dog cannot be cast to java.lang.Comparable
    at java.util.TreeMap.put(Unknown Source)
    at collection.TestHashMap.main(TestHashMap.java:35)

因为TreeMap按照键的顺序进行排列对象,所以键的对象之间需要能够比较,所以就要实现Comparable接口。你可以使用String作为键,String已经实现了Comparable接口。

我们来修改下Dog类,让它实现Comparable接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
classDogimplementsComparable<Dog>{
    String color;
    intsize;
 
    Dog(String c,ints) {
        color = c;
        size = s;
    }
 
    publicString toString(){  
        returncolor +" dog";
    }
 
    @Override
    publicintcompareTo(Dog o) {
        return o.size -this.size;
    }
}
 
publicclassTestTreeMap {
    publicstaticvoidmain(String[] args) {
        Dog d1 =newDog("red",30);
        Dog d2 =newDog("black",20);
        Dog d3 =newDog("white",10);
        Dog d4 =newDog("white",10);
 
        TreeMap treeMap =newTreeMap();
        treeMap.put(d1,10);
        treeMap.put(d2,15);
        treeMap.put(d3,5);
        treeMap.put(d4,20);
 
        for(Entry entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() +" - "+ entry.getValue());
        }
    }
}

输出:

1
2
3
red dog - 10
black dog - 15
white dog - 20

结果根据键的排列顺序进行输出,在我们的例子中根据size排序的。

如果我们将“Dog d4 = new Dog(“white”, 10);”替换成“Dog d4 = new Dog(“white”, 40);”,那么输出会变成:

1
2
3
4
white dog - 20
red dog - 10
black dog - 15
white dog - 5

这是因为TreeMap使用compareTo()方法来比较键值的大小,size不相等的狗是不同的狗,如果颜色不同但是size相同则也算相同,具体看Dog类的cmpareTo()函数的实现。

4. Hashtable

Java文档写道:

HashMap类和Hashtable类几乎相同,不同之处在于HashMap是不同步的,也允许接受null键和null值。

5. LinkedHashMap

LinkedHashMap is a subclass of HashMap. That means it inherits the features of HashMap. In addition, the linked list preserves the insertion-order.

Let’s replace the HashMap with LinkedHashMap using the same code used for HashMap.

LinkedHashMap是HashMap的子类,所以LinkedHashMap继承了HashMap的一些属性,它在HashMap基础上增加的特性就是保存了插入对象的顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
classDog {
    String color;
 
    Dog(String c) {
        color = c;
    }
 
    publicbooleanequals(Object o) {
        return((Dog) o).color ==this.color;
    }
 
    publicinthashCode() {
        returncolor.length();
    }
 
    publicString toString(){  
        returncolor +" dog";
    }
}
 
publicclassTestHashMap {
    publicstaticvoidmain(String[] args) {
 
        Dog d1 =newDog("red");
        Dog d2 =newDog("black");
        Dog d3 =newDog("white");
        Dog d4 =newDog("white");
 
        LinkedHashMap linkedHashMap =newLinkedHashMap();
        linkedHashMap.put(d1,10);
        linkedHashMap.put(d2,15);
        linkedHashMap.put(d3,5);
        linkedHashMap.put(d4,20);
 
        for(Entry entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey() +" - "+ entry.getValue());
        }      
    }
}

输出:

1
2
3
red dog - 10
black dog - 15
white dog - 20

如果我们使用HashMap的话,输出将会如下,会打乱插入的顺序:

1
2
3
red dog - 10
white dog - 20
black dog - 15

我们今天的关于HashMap、HashTable、LinkedHashMap和TreeMap用法和区别hashmap,linkedmap,treemap的区别的分享就到这里,谢谢您的阅读,如果想了解更多关于CurrentHashMap、HashMap、HashTable的区别、hashmap hashtable LinkedHashMap TreeMap、HashMap vs TreeMap vs Hashtable vs LinkedHashMap、HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMa的相关信息,可以在本站进行搜索。

本文标签: