map,set,list等集合解析以及HashMap,LinkedHashMap,TreeMap等该选谁的的区别

前言:
      今天在整理一些资料时,想起了map,set,list等集合,于是就做些笔记,提供给大家学习参考以及自己日后回顾。

首先Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。其中最常用的几种map如下:

Hashmap: 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

Hashtable:与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。

LinkedHashMap:保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

TreeMap:实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

一般情况下,我们用的最多的是HashMap,HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。

TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键值,那么TreeMap会更好。

LinkedHashMap 是HashMap的一个子类,如果需要输出顺序和输入的顺序相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。

 

 

1. HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key

2. Map的key和Set都有一个共同的特性就是集合的唯一性.TreeMap更是多了一个排序的功能.

3. hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可.

   a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.
   b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象
       才可以真正定位到键值对应的Entry.
   c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value

4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的.
   a. Comparator可以在创建TreeMap时指定
   b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.
   c. TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了.

 

 

注意: 
1、Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。 

2、Set和Collection拥有一模一样的接口。 

3、List,可以通过get()方法来一次取出一个元素。使用数字来选择一堆对象中的一个,get(0)...。(add/get) 

4、一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。 

5、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value。 
      HashMap会利用对象的hashCode来快速找到key。 
    *     hashing 
          哈希码就是将对象的信息经过一些转变形成一个独一无二的int值,这个值存储在一个array中。 
          我们都知道所有存储结构中,array查找速度是最快的。所以,可以加速查找。 
      
          发生碰撞时,让array指向多个values。即,数组每个位置上又生成一个梿表。 

6、Map中元素,可以将key序列、value序列单独抽取出来。 
使用keySet()抽取key序列,将map中的所有keys生成一个Set。 
使用values()抽取value序列,将map中的所有values生成一个Collection。 
为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。 

 

其次是map,set,list等java中集合解析:

在JAVA的util包中有两个所有集合的父接口Collection和Map,它们的父子关系: 
           java.util 
        +Collection 这个接口extends自 --java.lang.Iterable接口 
           +List 接口 
              -ArrayList 类 
              -LinkedList 类 
              -Vector 类     此类是实现同步的 
           +Queue 接口 
              +不常用,在此不表. 
           +Set 接口 
              +SortedSet 接口 
                 -TreeSet 类 
              -HashSet 
        +Map 接口 
          -HashMap 类 (除了不同步和允许使用 null 键/值之外,与 Hashtable 大致相同.) 
          -Hashtable 类 此类是实现同步的,不允许使用 null 键值 
          +SortedMap 接口 
             -TreeMap 类 
以下对众多接口和类的简单说明:首先不能不先说一下数组(Array) 
      一、Array , Arrays 
Java所有“存储及随机访问一连串对象”的做法,array是最有效率的一种。 
1、 效率高,但容量固定且无法动态改变。 
array还有一个缺点是,无法判断其中实际存有多少元素,length只是告诉我们array的容量。 

2、Java中有一个Arrays类,专门用来操作array。 
       arrays中拥有一组static函数, 
equals():比较两个array是否相等。array拥有相同元素个数,且所有对应元素两两相等。 
fill():将值填入array中。 
sort():用来对array进行排序。 
binarySearch():在排好序的array中寻找元素。 
System.arraycopy():array的复制。 

      二、Collection , Map 
若撰写程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用容器类库,array不适用。 
1、Collection 和 Map 的区别 
容器内每个为之所存储的元素个数不同。 
Collection类型者,每个位置只有一个元素。 
Map类型者,持有 key-value pair,像个小型数据库。 

2、Java2容器类类库的用途是“保存对象”,它分为两类,各自旗下的子类关系 
Collection 
       --List:它确保维护元素特定的顺序.            
             --ArrayList / LinkedList / Vector 
       --Set :不能含有重复的元素 
             --HashSet /TreeSet 
Map 
       --HashMap 
    --HashTable 
    --TreeMap 
 Map----一组成对的“键值对”对象,即其元素是成对的对象,最典型的应用就是数据字典,并且还有其它广泛的应用。另外,Map可以返回其所 有键组成的Set和其所有值组成的Collection,或其键值对组成的Set,并且还可以像数组一样扩展多维Map,只要让Map中键值对的每个 “值”是一个Map即可。 

 Collection下 1.迭代器 

  迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。 

  Java中的Iterator功能比较简单,并且只能单向移动: 

  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。 


  (2) 使用next()获得序列中的下一个元素。 

  (3) 使用hasNext()检查序列中是否还有元素。 

  (4) 使用remove()将迭代器新返回的元素删除。 

  Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。 

  2.List的功能方法 

  List(interface): 次序是List最重要的特点;它确保维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(只推荐 LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和删除元素。 

  ArrayList: 由数组实现的List。它允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历ArrayList,而不是用来插入和删除元素,因为这比LinkedList开销要大很多。 

  LinkedList: 由列表实现的List。对顺序访问进行了优化,向List中间插入与删除得开销不大,随机访问则相对较慢(可用ArrayList代替)。它具有方法 addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast(),这些方 法(没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。 

  3.Set的功能方法 

  Set(interface): 存入Set的每个元素必须是唯一的,这也是与List不同的,因为Set不保存重复元素。加入Set的Object必须定义equals()方法以确保对 象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。 

  HashSet: HashSet能快速定位一个元素,存入HashSet的对象必须定义hashCode()。 

  TreeSet: 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。 

  LinkedHashSet: 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。 

  HashSet采用散列函数对元素进行排序,这是专门为快速查询而设计的;TreeSet采用红黑树的数据结构进行排序元 素;LinkedHashSet内部使用散列以加快查询速度,同时使用链表维护元素的次序,使得看起来元素是以插入的顺序保存的。需要注意的是,生成自己 的类时,Set需要维护元素的存储顺序,因此要实现Comparable接口并定义compareTo()方法。 
4.Map的功能方法 
java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap 
Map主要用于存储健值对,根据键得到值,因此不允许键重复,但允许值重复。 
Hashmap 是一个 最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力. 
Hashtable 与 HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。 
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。 
TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
public   class   TestMap { 
    
  /**
   * 初始化一个Map
   * @param map
   */ 
  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" );   } 
  
 
  /**
   * 输出一个Map
   * @param map
   */ 
  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()); 
   
  
 
  /**
   * 判断map是否包含某个键
   * @param map
   * @param key
   * @return
   */ 
  public   static   boolean   containsKey(Map map, Object key){ 
   if   (map !=  null ){ 
    return   map.containsKey(key); 
  
   return   false
 
  /**
   * 判断map是否包含某个值
   * @param map
   * @param value
   * @return
   */ 
  public   static   boolean   containsValue(Map map, Object value){ 
   if   (map !=  null ){ 
    return   map.containsValue(value); 
  
   return   false
 
  /**
   * 演示HashMap
   */ 
  public   static   void   testHashMap(){ 
   Map myMap =  new   HashMap(); 
   init(myMap); 
   //HashMap的键可以为null 
   myMap.put( null , "ddd" ); 
   //HashMap的值可以为null 
   myMap.put( "aaa" null ); 
   output(myMap); 
 
  /**
   * 演示Hashtable
   */ 
  public   static   void   testHashtable(){ 
   Map myMap =  new   Hashtable(); 
   init(myMap); 
   //Hashtable的键不能为null 
   //myMap.put(null,"ddd"); 
   //Hashtable的值不能为null 
   //myMap.put("aaa", null); 
   output(myMap); 
 
  /**
   * 演示LinkedHashMap
   */ 
  public   static   void   testLinkedHashMap(){ 
   Map myMap =  new   LinkedHashMap(); 
   init(myMap); 
   //LinkedHashMap的键可以为null 
   myMap.put( null , "ddd" ); 
   //LinkedHashMap的值可以为null 
   myMap.put( "aaa" null ); 
   output(myMap); 
 
  /**
   * 演示TreeMap
   */ 
  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()); 

  

3、其他特征 
*     List,Set,Map将持有对象一律视为Object型别。 
*     Collection、List、Set、Map都是接口,不能实例化。 
      继承自它们的 ArrayList, Vector, HashTable, HashMap是具象class,这些才可被实例化。 
*     vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。 

三、Collections 
Collections是针对集合类的一个帮助类。提供了一系列静态方法实现对各种集合的搜索、排序、线程完全化等操作。 
相当于对Array进行类似操作的类——Arrays。 
如,Collections.max(Collection coll); 取coll中最大的元素。 
       Collections.sort(List list); 对list中元素排序 


四、如何选择? 
1、容器类和Array的区别、择取 
      *     容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。 
      *     一旦将对象置入容器内,便损失了该对象的型别信息。 
2、 
     *     在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList(); 
        Vector总是比ArrayList慢,所以要尽量避免使用。 
     *     在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。 
        HashTree存在的唯一理由:能够维护其内元素的排序状态。 
     *     在各种Maps中 
        HashMap用于快速查找。 
     *     当元素个数固定,用Array,因为Array效率是最高的。

 
结论:最常用的是ArrayList,HashSet,HashMap,Array。而且,我们也会发现一个规律,用TreeXXX都是排序的。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值