Collection,List ,Set 和Map 用法和区别

首先看一下他们之间的关系 

Collection          接口的接口   对象的集合 
├ List                   子接口      按进入前后有序保存   可重复 
│├ LinkedList                接口实现类   链表   插入删除   没有同步   线程不安全 
│├ ArrayList                  接口实现类   数组   随机访问   没有同步   线程不安全 
│└ Vector                      接口实现类   数组                  同步        线程安全 
│   └ Stack 
└ Set                   子接口       仅接收一次,并作内部排序 

├ HashSet 

│   └ LinkedHashSet 
└ TreeSet 


对于 List ,关心的是顺序, 它保证维护元素特定的顺序(容许有相同元素),使用此接口可以精确的控制每一个元素插入的位置。用户可以使用索引(元素在 List 中的位置,相似于数组下标)来访问 List 中的元素。 

对于 Set ,只关心某元素是否属于 Set (不 容许有相同元素 ),而不关心它的顺序。 

Map                接口      键值对的集合 
├ Hashtable                  接口实现类                  同步           线程安全 
├ HashMap                   接口实现类                  没有同步    线程不安全 

│├ LinkedHashMap 

│└ WeakHashMap 

├ TreeMap 
└ IdentifyHashMap 

对于 Map ,最大的特色是键值映射,且为一一映射,键不能重复,值能够,因此是用键来索引值。 方法 put(Object key, Object value) 添加一个“值” ( 想要得东西 ) 和与“值”相关联的“键” (key) ( 使用它来查找 ) 。方法 get(Object key) 返回与给定“键”相关联的“值”。 

Map 一样对每一个元素保存一份,但这是基于 " 键 " 的, Map 也有内置的排序,于是不关心元素添加的顺序。若是添加元素的顺序对你很重要,应该使用 LinkedHashSet 或者 LinkedHashMap. 

对于效率, Map 因为采用了哈希散列,查找元素时明显比 ArrayList 快。 

  
但我有一个本身的原则想法:复杂的问题简单化。即把不少晦涩难懂的问题用通俗直白的话,一会儿就看明白了,而不是大段大段的写。不得不指出的是现 在部分所谓的“专家”每每把简单的问题复杂化,让人看了生畏,甚至望而却步,以此来显示他的高深莫测,固然也可能有别的用意,那我就不得而知了。 


更为精炼的总结: 

Collection 是对象集合, Collection 有两个子接口 List 和 Set 

List 能够经过下标 (1,2..) 来取得值,值能够重复 

而 Set 只能经过游标来取值,而且值是不能重复的 

ArrayList , Vector , LinkedList 是 List 的实现类 

ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的 

LinkedList 是线程不安全的,底层是由链表实现的   

Map 是键值对集合 

HashTable 和 HashMap 是 Map 的实现类   
HashTable 是线程安全的,不能存储 null 值   
HashMap 不是线程安全的,能够存储 null 值   
  

因此,若是你是想在一个很短的时间来弄明白这些问题,好比 1~2 分钟。没有也不想花大量时间于此,那么建议你如今就能够收兵走人了。 

  
若是你想对此作一个详细的了解,请继续看下去。 

  
众所周知, Java 来源于 C++ ,屏蔽了其底层实现,简化了对底层实现的管理,使开发者专一于上层功能的实现。在 C/C++ 里关于数据的存储须要程序员很是清楚,而 Java 程序员能够彻底无论这些,那么, Java 是怎么管理的呢?其实 Java 仍是须要面临这些问题,只不过通过封装后,变得面目全非。因此对于像我这种从 C/C++ 转向 Java 的人还须要一段时间适应, Collection 、 List 、 Set 、 Map 等概念还须要一个接受的过程。其实到后来发现,无论是什么语言,其底层存储不外乎数组、线性表、栈、队列、串、树和图等数据结构。想明白了这些,一切都敞 亮了。 

  
1、容器( Collection ) 接口 
   容器( Collection )是最基本的集合接口,一个容器( Collection )保存一组对象( Object ),即对象是容器的元素( Elements )。一些 Collection 容许相同的元素而另外一些不行。一些能排序而另外一些不行。 Java SDK 不提供直接继承自 Collection 的类, Java SDK 提供的类都是继承自 Collection 的 “ 子接口 ” 如 List 和 Set 。 
  全部实现 Collection 接口的类都必须提供两个标准的构造函数:无参数的构造函数用于建立一个空的 Collection ,有一个 Collection 参数的构造函数用于建立一个新的 Collection ,这个新的 Collection 与传入的 Collection 有相同的元素。后一个构造函数容许用户复制一个 Collection 。 
  如何遍历 Collection 中的每个元素?不论 Collection 的实际类型如何,它都支持一个 iterator() 的方法,该方法返回一个迭代子,使用该迭代子便可逐一访问 Collection 中每个元素。典型的用法以下: 
     Iterator it = collection.iterator(); // 得到一个迭代子 
     while(it.hasNext()) { 
       Object obj = it.next(); // 获得下一个元素 
     } 


由 Collection 接口派生的两个接口是 List 和 Set 。 List 按对象进入的顺序保存对象,不作排序或编辑操做。 Set 对每一个对象只接受一次,并使用本身内部的排序方法 ( 一般,你只关心某个元素是否属于 Set, 而不关心它的顺序 -- 不然应该使用 List) 。 


1 , List 接口 
   List 是有序的 Collection ,次序是 List 最重要的特色:它保证维护元素特定的顺序。使用此接口可以精确的控制每一个元素插入的位置。用户可以使用索引(元素在 List 中的位置,相似于数组下标)来访问 List 中的元素,这相似于 Java 的数组。和下面要提到的 Set 不一样, List 容许有相同的元素。 
   除了具备 Collection 接口必备的 iterator() 方法外, List 还提供一个 listIterator() 方法,返回一个 ListIterator 接口,和标准的 Iterator 接口相比, ListIterator 多了一些 add() 之类的方法,容许添加,删除,设定元素, 还能向前或向后遍历。 
  实现 List 接口的经常使用类有 LinkedList , ArrayList , Vector 和 Stack 。其中,最经常使用的是 LinkedList 和 ArrayList 两个。 

LinkedList 类 
    LinkedList 实现了 List 接口,容许 null 元素。此外 LinkedList 提供额外的 addFirst(), addLast(), getFirst(), getLast(), removeFirst(), removeLast(), insertFirst(), insertLast() 方法在 LinkedList 的首部或尾部,这些方法(没有在任何接口或基类中定义过)使 LinkedList 可被用做堆栈( stack ),队列( queue )或双向队列( deque )。 

注意 LinkedList 没有同步方法。若是多个线程同时访问一个 List ,则必须本身实现访问同步。一种解决方法是在建立 List 时构造一个同步的 List : 
     List list = Collections.synchronizedList(new LinkedList(...)); 

特色:对顺序访问进行了优化,向 List 中间插入与删除的开销并不大。随机访问则相对较慢。 ( 使用 ArrayList 代替。 ) 

ArrayList 类 
   ArrayList 是由数组实现的 List ,而且实现了可变大小的数组。它容许全部元素,包括 null 。 ArrayList 没有同步。 size , isEmpty , get , set 方法运行时间为常数。可是 add 方法开销为分摊的常数,添加 n 个元素须要 O(n) 的时间。其余的方法运行时间为线性。 
   每一个 ArrayList 实例都有一个容量( Capacity ),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增长,可是增加算法并无定义。当须要插入大量元素时,在插入前能够调用 ensureCapacity 方法来增长 ArrayList 的容量以提升插入效率。 
  和 LinkedList 同样, ArrayList 也是非同步的( unsynchronized )。 

  
特色:容许对元素进行快速随机访问,可是向 List 中间插入与移除元素的速度很慢。 ListIterator 只应该用来由后向前遍历 ArrayList, 而不是用来插入和移除元素。由于那比 LinkedList 开销要大不少。 

  
Vector 类 
    Vector 很是相似 ArrayList ,可是 Vector 是同步的。由 Vector 建立的 Iterator ,虽然和 ArrayList 建立的 Iterator 是同一接口,可是,由于 Vector 是同步的,当一个 Iterator 被建立并且正在被使用,另外一个线程改变了 Vector 的状态(例如,添加或删除了一些元素),这时调用 Iterator 的方法时将抛出 ConcurrentModificationException ,所以必须捕获该异常。 

     Stack 类: Stack 继承自 Vector ,实现一个后进先出的堆栈。 Stack 提供 5 个额外的方法使得 Vector 得以被看成堆栈使用。基本的 push 和 pop 方法,还有 peek 方法获得栈顶的元素, empty 方法测试堆栈是否为空, search 方法检测一个元素在堆栈中的位置。 Stack 刚建立后是空栈。 

2 , Set 接口 
   Set 具备与 Collection 彻底同样的接口,所以没有任何额外的功能,不像前面有几个不一样的 List 。实际上 Set 就是 Collection ,只是行为不一样。(这是继承与多态思想的典型应用:表现不一样的行为)。其次, Set 是一种不包含重复的元素的 Collection ,加入 Set 的元素必须定义 equals() 方法以确保对象的惟一性 ( 即任意的两个元素 e1 和 e2 都有 e1.equals(e2)=false ),与 List 不一样的是, Set 接口不保证维护元素的次序。最后, Set 最多有一个 null 元素。 
  很明显, Set 的构造函数有一个约束条件,传入的 Collection 参数不能包含重复的元素。 
  请注意:必须当心操做可变对象( Mutable Object )。若是一个 Set 中的可变元素改变了自身状态致使 Object.equals(Object)=true 将致使一些问题。 


HashSet 类 

为快速查找设计的 Set 。存入 HashSet 的对象必须定义 hashCode() 。 

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

TreeSet 类 

保存次序的 Set, 底层为树结构。使用它能够从 Set 中提取有序的序列。 


2、 Map 接口 





经常使用操做说明 

  void clear() 

  今后映射中移除全部映射关系(可选操做)。 

  boolean containsKey(Object key) 

  若是此映射包含指定键的映射关系,则返回 true。 

  boolean containsValue(Object value) 

  若是此映射将一个或多个键映射到指定值,则返回 true。 

  Set<Map.Entry<K,V>> entrySet() 

  返回此映射中包含的映射关系的 Set 视图。 

  boolean equals(Object o) 

  比较指定的对象与此映射是否相等。 

  V get(Object key) 

  返回指定键所映射的值;若是此映射不包含该键的映射关系,则返回 null。 

  int hashCode() 

  返回此映射的哈希码值。 

  boolean isEmpty() 

  若是此映射未包含键-值映射关系,则返回 true。 

  Set<K> keySet() 

  返回此映射中包含的键的 Set 视图。 

  V put(K key, V value) 

  将指定的值与此映射中的指定键关联(可选操做)。 

  void putAll(Map<? extends K,? extends V> m) 

  从指定映射中将全部映射关系复制到此映射中(可选操做)。 

  V remove(Object key) 

  若是存在一个键的映射关系,则将其今后映射中移除(可选操做)。 

  int size() 

  返回此映射中的键-值映射关系数。 

  Collection<V> values() 

  返回此映射中包含的值的 Collection 视图。 

Map的通常用法 

1.声明一个Map : 

   Map map = new HashMap(); 

2 .向map中放值 ,注意: map是key-value的形式存放的,如: 

       map.put("sa","dd"); 

3 .从map中取值 : 

    String str = map.get("sa").toString, 

    结果是: str = "dd' 

4 .遍历一个map,从中取得key和value : 

    Map m= new HashMap(); 

    for(Object obj : map.keySet()){ 

          Object value = map.get(obj ); 

    } 


   请注意, Map 没有继承 Collection 接口, Map 提供 key 到 value 的映射,你能够经过“键”查找“值”。一个 Map 中不能包含相同的 key ,每一个 key 只能映射一个 value 。 Map 接口提供 3 种集合的视图, Map 的内容能够被看成一组 key 集合,一组 value 集合,或者一组 key-value 映射。 

方法 put(Object key, Object value) 添加一个“值” ( 想要得东西 ) 和与“值”相关联的“键” (key) ( 使用它来查找 ) 。方法 get(Object key) 返回与给定“键”相关联的“值”。能够用 containsKey() 和 containsValue() 测试 Map 中是否包含某个“键”或“值”。 标准的 Java 类库中包含了几种不一样的 Map : HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap 。它们都有一样的基本接口 Map ,可是行为、效率、排序策略、保存对象的生命周期和断定“键”等价的策略等各不相同。 

Map 一样对每一个元素保存一份,但这是基于 " 键 " 的, Map 也有内置的排序,于是不关心元素添加的顺序。若是添加元素的顺序对你很重要,应该使用 LinkedHashSet 或者 LinkedHashMap. 

执行效率是 Map 的一个大问题。看看 get() 要作哪些事,就会明白为何在 ArrayList 中搜索“键”是至关慢的。而这正是 HashMap 提升速度的地方。 HashMap 使用了特殊的值,称为“散列码” (hash code) ,来取代对键的缓慢搜索。“散列码”是“相对惟一”用以表明对象的 int 值,它是经过将该对象的某些信息进行转换而生成的(在下面总结二:须要的注意的地方有更进一步探讨)。全部 Java 对象都能产生散列码,由于 hashCode() 是定义在基类 Object 中的方法 。 HashMap 就是使用对象的 hashCode() 进行快速查询的。此方法可以显著提升性能。 


Hashtable 类 
   Hashtable 继承 Map 接口,实现一个 key-value 映射的哈希表。任何非空( non-null )的对象均可做为 key 或者 value 。 
  添加数据使用 put(key, value) ,取出数据使用 get(key) ,这两个基本操做的时间开销为常数。 
    Hashtable 经过初始化容量 (initial capacity) 和负载因子 (load factor) 两个参数调整性能。一般缺省的 load factor 0.75 较好地实现了时间和空间的均衡。增大 load factor 能够节省空间但相应的查找时间将增大,这会影响像 get 和 put 这样的操做。 
    使用 Hashtable 的简单示例以下,将 1 , 2 , 3 放到 Hashtable 中,他们的 key 分别是 ”one” , ”two” , ”three” : 
     Hashtable numbers = new Hashtable(); 
     numbers.put(“one”, new Integer(1)); 
     numbers.put(“two”, new Integer(2)); 
     numbers.put(“three”, new Integer(3)); 
  要取出一个数,好比 2 ,用相应的 key : 
     Integer n = (Integer)numbers.get(“two”); 
     System.out.println(“two = ” + n); 
   因为做为 key 的对象将经过计算其散列函数来肯定与之对应的 value 的位置,所以任何做为 key 的对象都必须实现 hashCode 方法和 equals 方法。 hashCode 方法和 equals 方法继承自根类 Object ,若是你用自定义的类看成 key 的话,要至关当心,按照散列函数的定义,若是两个对象相同,即 obj1.equals(obj2)=true ,则它们的 hashCode 必须相同,但若是两个对象不一样,则它们的 hashCode 不必定不一样,若是两个不一样对象的 hashCode 相同,这种现象称为冲突,冲突会致使操做哈希表的时间开销增大,因此尽可能定义好的 hashCode() 方法,能加快哈希表的操做。 
  若是相同的对象有不一样的 hashCode ,对哈希表的操做会出现意想不到的结果(期待的 get 方法返回 null ),要避免这种问题,只须要牢记一条:要同时复写 equals 方法和 hashCode 方法,而不要只写其中一个。 
   Hashtable 是同步的。 

HashMap 类 
    HashMap 和 Hashtable 相似,也是基于散列表的实现。不一样之处在于 HashMap 是非同步的,而且容许 null ,即 null value 和 null key 。将 HashMap 视为 Collection 时( values() 方法可返回 Collection ),插入和查询“键值对”的开销是固定的,但其迭代子操做时间开销和 HashMap 的容量成比例。所以,若是迭代操做的性能至关重要的话,不要将 HashMap 的初始化容量 (initial capacity) 设得太高,或者负载因子 (load factor) 太低。 

   LinkedHashMap 类:相似于 HashMap ,可是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用 (LRU) 的次序。只比 HashMap 慢一点。而在迭代访问时发而更快,由于它使用链表维护内部次序。 

WeakHashMap 类:弱键( weak key ) Map 是一种改进的 HashMap ,它是为解决特殊问题设计的,对 key 实行 “ 弱引用 ” ,若是一个 key 再也不被外部所引用(没有 map 以外的引用),那么该 key 能够被垃圾收集器 (GC) 回收。 

TreeMap 类 

基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序 ( 次序由 Comparabel 或 Comparator 决定 ) 。 TreeMap 的特色在于,你获得的结果是通过排序的。 TreeMap 是惟一的带有 subMap() 方法的 Map ,它能够返回一个子树。 

IdentifyHashMap 类 

使用 == 代替 equals() 对“键”做比较的 hash map 。专为解决特殊问题而设计。 


总结一:比较 

1 ,数组 (Array) ,数组类 (Arrays) 

Java 全部“存储及随机访问一连串对象”的作法, array 是最有效率的一种。但缺点是容量固定且没法动态改变。 array 还有一个缺点是,没法判断其中实际存有多少元素, length 只是告诉咱们 array 的容量。 


Java 中有一个数组类 (Arrays) ,专门用来操做 array 。数组类 (arrays) 中拥有一组 static 函数。 

equals() :比较两个 array 是否相等。 array 拥有相同元素个数,且全部对应元素两两相等。 

fill() :将值填入 array 中。 

sort() :用来对 array 进行排序。 

binarySearch() :在排好序的 array 中寻找元素。 

System.arraycopy() : array 的复制。 

  
若编写程序时不知道究竟须要多少对象,须要在空间不足时自动扩增容量,则须要使用容器类库, array 不适用。 

  
2 ,容器类与数组的区别 

容器类仅能持有对象引用(指向对象的指针),而不是将对象信息 copy 一份至数列某位置。一旦将对象置入容器内,便损失了该对象的型别信息。 


3 ,容器 (Collection) 与 Map 的联系与区别 

Collection 类型,每一个位置只有一个元素。 

Map 类型,持有 key-value 对 (pair) ,像个小型数据库。 

  
Collections 是针对集合类的一个帮助类。提供了一系列静态方法实现对各类集合的搜索、排序、线程彻底化等操做。至关于对 Array 进行相似操做的类—— Arrays 。 

如, Collections.max(Collection coll); 取 coll 中最大的元素。 

    Collections.sort(List list); 对 list 中元素排序 

  
List , Set , Map 将持有对象一概视为 Object 型别。 

Collection 、 List 、 Set 、 Map 都是接口,不能实例化。继承自它们的 ArrayList, Vector, HashTable, HashMap 是具象 class ,这些才可被实例化。 

vector 容器确切知道它所持有的对象隶属什么型别。 vector 不进行边界检查。 


总结二:须要注意的地方 

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

2 、 Set 和 Collection 拥有如出一辙的接口。但排除掉传入的 Collection 参数重复的元素。 

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

4 、 Map 用 put(k,v) / get(k) ,还可使用 containsKey()/containsValue() 来检查其中是否含有某个 key/value 。 

HashMap 会利用对象的 hashCode 来快速找到 key 。 

哈希码 (hashing) 就是将对象的信息通过一些转变造成一个独一无二的 int 值,这个值存储在一个 array 中。咱们都知道全部存储结构中, array 查找速度是最快的。因此,能够加速查找。发生碰撞时,让 array 指向多个 values 。即,数组每一个位置上又生成一个梿表。 

5 、 Map 中元素,能够将 key 序列、 value 序列单独抽取出来。 

使用 keySet() 抽取 key 序列,将 map 中的全部 keys 生成一个 Set 。 

使用 values() 抽取 value 序列,将 map 中的全部 values 生成一个 Collection 。 

为何一个生成 Set ,一个生成 Collection ?那是由于, key 老是独一无二的, value 容许重复。 


总结三:如何选择 
从效率角度: 

在各类 Lists ,对于须要快速插入,删除元素,应该使用 LinkedList (可用 LinkedList 构造堆栈 stack 、队列 queue ),若是须要快速随机访问元素,应该使用 ArrayList 。最好的作法是以 ArrayList 做为缺省选择。 Vector 老是比 ArrayList 慢,因此要尽可能避免使用。 

在各类 Sets 中, HashSet 一般优于 HashTree (插入、查找)。只有当须要产生一个通过排序的序列,才用 TreeSet 。 HashTree 存在的惟一理由:可以维护其内元素的排序状态。 

  
在各类 Maps 中 HashMap 用于快速查找。 

最后,当元素个数固定,用 Array ,由于 Array 效率是最高的。 

因此结论:最经常使用的是 ArrayList , HashSet , HashMap , Array 。 

  
更近一步分析: 

若是程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,若是多个线程可能同时操做一个类,应该使用同步的类。 
要特别注意对哈希表的操做,做为 key 的对象要同时正确复写 equals 方法和 hashCode 方法。 
尽可能返回接口而非实际的类型,如返回 List 而非 ArrayList ,这样若是之后须要将 ArrayList 换成 LinkedList 时,客户端代码不用改变。这就是针对抽象编程。程序员

 

企业级项目实战(带源码)地址http://zz563143188.iteye.com/blog/1825168算法

收集五年的开发资料下载地址:   http://pan.baidu.com/share/home?uk=4076915866&view=share
http://blog.csdn.net/jeamking/article/details/5836722
相关文章
相关标签/搜索