前言:Java中用不同的数据结构(哈希表/动态数组/平衡树/链表)实现了不同的集合接口(List/Map/Set)。大多数情况下,集合类被用于不需要线程安全地环境,所以后来的集合实现都没有再采用同步以便运行更快,而之前老的实现如Vector、HashSet则保留了同步特性。
List:List接口继承自Collection,是有序的(有序与排序不同,有序是指集合中元素保持插入时的顺序,而排序是指按对象大小安排位置)。
1.【动态数组】ArrayList:擅长随机元素访问,使用数组存储,ArrayList会比Vector快,他是非同步的,如果设计涉及到多线程,还是用Vector比较好一些。优势在于动态的增长数组,非常适合初始时总长度未知的情况下使用。实现了可变大小的数组。它允许所有元素,包括null。 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率;
2.【动态数组】Vector:其方法都是同步的,除非需要同步的场合,否则基本被其同门师弟ArrayList替代;
3.【动态数组】Stack:实现了先入后出(FILO),这种特性比较特殊,所以在新的集合库中没有直接对应的类;
4.【链表】LinkedList:双向列表存储,优势在于在中间位置插入和删除操作,速度是最快的。LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque);
Map:Map中的键是绝对不能重复的,实现了Map接口的集合类可以用来快速检索数据。建议使用不可变类作为Map的key(原因见下HashSet表述)。
1.【哈希表】HashMap:Map中访问速度最快。是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。允许将null作为一个entry的key或者value,而Hashtable不允许。把Hashtable的contains方法去掉了,改成containsvalue和containsKey,因为contains方法容易让人引起误解。当我们将键值对传递给 put ()方法时,它调用键对象的 hashCode ()方法来计算 hashcode,让后找到 bucket 位置来储存值对象。当获取对象时,通过键对象的 equals ()方法找到正确的键值对,然后返回值对象;
2.【哈希表】LinkedHashMap:是有序的(即按照插入顺序排列);
3.【哈希表】IdentityHashMap:
4.【哈希表】Hashtable:其方法都是同步的,除非需要同步的场合,否则基本被其同门师弟HashMap替代。继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。其方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步(如果是ArrayList:List lst = Collections.synchronizedList(new ArrayList());如果是HashMap:Map map = Collections.synchronizedMap(new HashMap());)。Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异;
5.【平衡树】TreeMap:实现了SortedMap,可以提供排序功能;
6.SortedMap:由TreeMap实现,元素是排序的,其键值对按键的升序排列,按照Map.Entry中key来排序。
Set:Set和Collection最大的区别是Set不能有重复的元素,而Collection可以。建议使用不可变类作为set的元素(原因见下HashSet表述)。
1.【哈希表】HashSet:Set中访问速度最快。原则上Set不能有重复的元素,但假设HashSet插入不同元素后,又将2个元素改成相同值也可以(这种属于未定义行为,即Set的各个实现可以自行处理这种情况,可以报异常也可以如HashSet般不闻不问,但是这种未定义行为应尽量避免,而避免的方法就是使得插入的元素不可修改,也即使得元素为不可变类,Map也有同样问题);
2.【哈希表】LinkedHashSet:是有序的(即按照插入顺序排列);
3.【平衡树】TreeSet:实现了SortedSet,可以提供排序功能;
4.SortedSet:由TreeSet实现,元素是排序的,按升序排列的(排序与有序不同,见上述)。
小结:对于查找、插入和删除,哈希表的平均性能优于平衡树,因为对于查找而言后者要在平衡树的分支中不断查找,对于增删后者要不断调整数结构以维持平衡。
=========================================================
Iterator和ListIterator的区别:
- ListIterator有add()方法,可以向List中添加对象,而Iterator不能;
- ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以;
- ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能;
- 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。
Hashmap如何同步,有两种选择:
- 使用Collections.synchronizedMap(..)来同步HashMap;
- 使用ConcurrentHashMap,首选是使用ConcurrentHashMap,这是因为我们不需要锁定整个对象,以及通过ConcurrentHashMap分区地图来获得锁。
IdentityHashMap和HashMap的区别:
- 在HashMap中如果两个元素是相等的,则key1.equals(key2);
- 在IdentityHashMap中如果两个元素是相等的,则key1 == key2。
小结:Iterator是所有集合间的粘合剂,优点在于被所有集合类型所支持,可以编写通用的代码。
=========================================================
选择不同数据结构的关注点:查询、插入、删除、元素的唯一性、元素的次序、元素是否有必须定义的方法、线程安全性