ArrayList、LinkedList、 Vector、Map 用法比较 
ArrayList和Vector是采用数组方式存储数据,此数组元素总数大于实际存储的数据个数以便增加和插入元素,二者都允许直接序号索引元素,但是插入数据要移动数组元素等内存操作,所以它们索引数据快、插入数据慢。
ArrayList数组存储方式:
[java]view plaincopyprint?
- privatetransient Object[] elementData; 
- public ArrayList(int initialCapacity) { 
-       super(); 
-       if (initialCapacity < 0) 
-       thrownew IllegalArgumentException("Illegal Capacity: " + initialCapacity); 
-       this.elementData = new Object[initialCapacity]; 
- } 
- // 空构造函数,默认容量大小为10 
- public ArrayList() { 
-       this(10); 
- } 
- public ArrayList(Collection<? extends E> c) { 
-        elementData = c.toArray(); 
-        size = elementData.length; 
-       // c.toArray might (incorrectly) not return Object[] (see 6260652) 
-       if (elementData.getClass() != Object[].class) 
-        elementData = Arrays.copyOf(elementData, size, Object[].class); 
- } 
private transient Object[] elementData;
public ArrayList(int initialCapacity) {
          super();
          if (initialCapacity < 0)
              throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
          this.elementData = new Object[initialCapacity];
}
// 空构造函数,默认容量大小为10
public ArrayList() {
          this(10);
}
public ArrayList(Collection<? extends E> c) {
          elementData = c.toArray();
          size = elementData.length;
          // c.toArray might (incorrectly) not return Object[] (see 6260652)
          if (elementData.getClass() != Object[].class)
              elementData = Arrays.copyOf(elementData, size, Object[].class);
}
Vector由于使用了synchronized同步方法(如add、insert、remove、set、equals、hashcode等操作),因此是线程安全,性能上比ArrayList要差。
Vector数组存储方式:
[java]view plaincopyprint?
- protected Object[] elementData; 
- protectedint capacityIncrement; 
- public Vector(int initialCapacity, int capacityIncrement) { 
-       super(); 
-       if (initialCapacity < 0) 
-       thrownew IllegalArgumentException("Illegal Capacity: "+ initialCapacity); 
-       this.elementData = new Object[initialCapacity]; 
-       this.capacityIncrement = capacityIncrement; 
- } 
- // 设置容量大小为initialCapacity,默认增长个数为0 
- public Vector(int initialCapacity) { 
-       this(initialCapacity, 0); 
- } 
- // 空构造函数,默认容量大小为10 
- public Vector() { 
-       this(10); 
- } 
protected Object[] elementData;
protected int capacityIncrement;
public Vector(int initialCapacity, int capacityIncrement) {
          super();
          if (initialCapacity < 0)
              throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
          this.elementData = new Object[initialCapacity];
          this.capacityIncrement = capacityIncrement;
}
// 设置容量大小为initialCapacity,默认增长个数为0
public Vector(int initialCapacity) {
          this(initialCapacity, 0);
}
// 空构造函数,默认容量大小为10
public Vector() {
          this(10);
}
LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!
LinkedList双向链表,是指可以从first依次遍历至last(从头到尾),也可以从last遍历至first(从尾到头),但首尾没有构成环,不同于双向循环链表(注意区分):
[java]view plaincopyprint?
- transient Node<E> first; 
- transient Node<E> last; 
- public LinkedList() { 
- } 
- privatevoid linkFirst(E e) { 
-       final Node<E> f = first; 
-       final Node<E> newNode = new Node<>(null, e, f); // 插入新节点,同时连接首、尾节点 
-        first = newNode; 
-       if (f == null) // 起始节点为空(null),表示插入后有且只有一个节点,因此first = last = newNode 
-        last = newNode; 
-       else
-        f.prev = newNode; 
-        size++; 
-        modCount++; 
-  } 
- void linkLast(E e) { 
-       final Node<E> l = last; 
-       final Node<E> newNode = new Node<>(l, e, null); // 插入新节点,同时连接首、尾节点 
-        last = newNode; 
-       if (l == null) // 末尾节点为空(null),表示插入后有且只有一个节点,因此first = last = newNode 
-        first = newNode; 
-       else
-        l.next = newNode; 
-        size++; 
-        modCount++; 
-  } 
 transient Node<E> first;
transient Node<E> last;
public LinkedList() {
}
private void linkFirst(E e) {
              final Node<E> f = first;
              final Node<E> newNode = new Node<>(null, e, f);  // 插入新节点,同时连接首、尾节点
              first = newNode;
              if (f == null)   // 起始节点为空(null),表示插入后有且只有一个节点,因此first = last = newNode
                  last = newNode;
              else
                  f.prev = newNode;
              size++;
              modCount++;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);  // 插入新节点,同时连接首、尾节点
last = newNode;
if (l == null)   // 末尾节点为空(null),表示插入后有且只有一个节点,因此first = last = newNode
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
其中,Node类结构如下:
[java]view plaincopyprint?
- privatestaticclass Node<E> { 
-        E item; 
-        Node<E> next; 
-        Node<E> prev; 
-       
-        Node(Node<E> prev, E element, Node<E> next) { 
-             this.item = element; // 节点数据 
-             this.next = next; // 连接下一节点 
-             this.prev = prev; // 连接上一节点 
-        } 
-  } 
 private static class Node<E> {
              E item;
              Node<E> next;
              Node<E> prev;
              Node(Node<E> prev, E element, Node<E> next) {
                        this.item = element; // 节点数据
                        this.next = next;  // 连接下一节点
                        this.prev = prev;  // 连接上一节点
              }
}
线性表、链表、哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构,这些类均在java.util包中。
Collection
|--------List
----------LinkedList
|----------ArrayList
|----------Vector
|-----Stack
|--------Set
|----------HashSet.
|-----LinkedHashSet
|----------SortedSet
|-----TreeSet
Collection.
● 实现该接口及其子接口的所有类都可应用clone()方法,并是序列化类. 
.....List.
.....● 可随机访问包含的元素 
.....● 元素是有序的 
.....● 可在任意位置增、删元素 
.....● 不管访问多少次,元素位置不变 
.....● 允许重复元素 
.....● 用Iterator实现单向遍历,也可用ListIterator实现双向遍历 
..........ArrayList
..........● 用数组作为根本的数据结构来实现List 
..........● 元素顺序存储 
..........● 新增元素改变List大小时,内部会新建一个数组,在将添加元素前将所有数据拷贝到新数组中 
..........● 随机访问很快,删除非头尾元素慢,新增元素慢而且费资源 
..........● 较适用于无频繁增删的情况 
..........● 比数组效率低,如果不是需要可变数组,可考虑使用数组 
..........● 非线程安全 
..........Vector.
..........● 另一种ArrayList,具备ArrayList的特性 
..........● 所有方法都是线程安全的(双刃剑,和ArrayList的主要区别) 
..........● 比ArrayList效率低 
...............Stack
...............● LIFO的数据结构 
..........LinkedList.
..........● 链接对象数据结构(类似链表) 
..........● 随机访问很慢,增删操作很快,不耗费多余资源 
..........● 非线程安全 
.....Set.
.....● 不允许重复元素,可以有一个空元素 
.....● 不可随机访问包含的元素 
.....● 只能用Iterator实现单向遍历 
..........HashSet
..........● 用HashMap作为根本数据结构来实现Set 
..........● 元素是无序的 
..........● 迭代访问元素的顺序和加入的顺序不同 
..........● 多次迭代访问,元素的顺序可能不同 
..........● 非线程安全 
...............LinkedHashSet
...............● 基于HashMap和链表的Set实现 
...............● 迭代访问元素的顺序和加入的顺序相同 
...............● 多次迭代访问,元素的顺序不变 
...............● 因此可说这是一种有序的数据结构 
...............● 性能比HashSet差 
...............● 非线程安全 
..........SortedSet
..........● 加入SortedSet的所有元素必须实现Comparable接口 
..........● 元素是有序的 
...............TreeSet.
...............● 基于TreeMap实现的SortedSet 
...............● 排序后按升序排列元素 
...............● 非线程安全 
Collection接口Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行,一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:
1)无参数的构造函数,用于创建一个空的Collection
2)有一个Collection参数的构造函数,用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。
后一个构造函数允许用户复制一个Collection。
如何遍历Collection中的每一个元素?
不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个接口是List和Set。
List接口
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。LinkedList类LinkedList实现了List接口,允许null元素。此外在LinkedList的首部或尾部提供额外的get、remove、insert方法。
这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
 List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList实现了可变大小的数组,它允许所有元素,包括null,没有同步。
size、isEmpty、get、set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间,其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
ArrayList扩展容量:
[java]view plaincopyprint?
- publicvoid ensureCapacity(int minCapacity) { // minCapacity: 期望设置的最小容量大小 
-       if (minCapacity > 0) 
-        ensureCapacityInternal(minCapacity); // 内部方法实现(private) 
-  } 
- privatevoid ensureCapacityInternal(int minCapacity) { 
-        modCount++; 
-       // overflow-conscious code 
-       if (minCapacity - elementData.length > 0) // 如果期望的最小容量minCapacity大于当前元素个数,则设置 
-        grow(minCapacity); 
-  } 
- privatestaticfinalint MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 
- privatevoid grow(int minCapacity) { 
-       // overflow-conscious code 
-       int oldCapacity = elementData.length; 
-       int newCapacity = oldCapacity + (oldCapacity >> 1); // 增长当前元素个数的1/2,即增长后为原先元素总量的3/2 
-       
-       if (newCapacity - minCapacity < 0) // 设置的minCapacity大于自动增长的容量,则按minCapacity最大设置(即取最大的容量) 
-        newCapacity = minCapacity; 
-       if (newCapacity - MAX_ARRAY_SIZE > 0) // 超过了VMs最大的内存分配空间(注:Integer.MAX_VALUE - 8;其中减去8字节是因为Integer附加信息额外占用了8个字节) 
-        newCapacity = hugeCapacity(minCapacity); 
-       // minCapacity is usually close to size, so this is a win: 
-        elementData = Arrays.copyOf(elementData, newCapacity); 
-  } 
- privatestaticint hugeCapacity(int minCapacity) { 
-       if (minCapacity < 0) // overflow 
-       thrownew OutOfMemoryError(); 
-       return (minCapacity > MAX_ARRAY_SIZE) ? 
-        Integer.MAX_VALUE : 
-        MAX_ARRAY_SIZE; 
-  } 
 public void ensureCapacity(int minCapacity) {  // minCapacity: 期望设置的最小容量大小
              if (minCapacity > 0)
                  ensureCapacityInternal(minCapacity);  // 内部方法实现(private)
}
private void ensureCapacityInternal(int minCapacity) {
              modCount++;
              // overflow-conscious code
              if (minCapacity - elementData.length > 0)  // 如果期望的最小容量minCapacity大于当前元素个数,则设置
                  grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
              // overflow-conscious code
              int oldCapacity = elementData.length;
              int newCapacity = oldCapacity + (oldCapacity >> 1);  // 增长当前元素个数的1/2,即增长后为原先元素总量的3/2
              if (newCapacity - minCapacity < 0)  // 设置的minCapacity大于自动增长的容量,则按minCapacity最大设置(即取最大的容量)
                  newCapacity = minCapacity;
              if (newCapacity - MAX_ARRAY_SIZE > 0) // 超过了VMs最大的内存分配空间(注:Integer.MAX_VALUE - 8;其中减去8字节是因为Integer附加信息额外占用了8个字节)
                  newCapacity = hugeCapacity(minCapacity);
              // minCapacity is usually close to size, so this is a win:
              elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
              if (minCapacity < 0) // overflow
                  throw new OutOfMemoryError();
              return (minCapacity > MAX_ARRAY_SIZE) ?
                  Integer.MAX_VALUE :
                  MAX_ARRAY_SIZE;
}
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
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刚创建后是空栈。
Set接口
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。Set 没有同步方法。
注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
Map
------Hashtable
------Properties
------HashMap
------LinkedHashMap
------WeakHashMap
------SortedMap
------TreeMap
Map
● 键值对,键和值一一对应 
● 不允许重复的键. 
.....Hashtable.
.....● 用作键的对象必须实现了hashcode()、equals()方法,也就是说只有Object及其子类可用作键 
.....● 键、值都不能是空对象 
.....● 多次访问,映射元素的顺序相同 
.....● 线程安全的 
..........Properties
..........● 键和值都是字符串 
.....HashMap
.....● 键和值都可以是空对象 
.....● 不保证映射的顺序 
.....● 多次访问,映射元素的顺序可能不同 
.....● 非线程安全 
...............LinkedHashMap
...............● 多次访问,映射元素的顺序是相同的 
...............● 性能比HashMap差 
.....WeakHashMap..
.....● 当某个键不再正常使用时,垃圾收集器会移除它,即便有映射关系存在 
.....● 非线程安全 
.....SortedMap.
.....● 键按升序排列 
.....● 所有键都必须实现.Comparable.接口. 
...............TreeMap.
...............● 基于红黑树的SortedMap实现 
...............● 非线程安全
Map接口
Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。
Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
Map接口定义:
[java]view plaincopyprint?
- publicinterface Map<K,V> { 
-       int size(); 
-       boolean isEmpty(); 
-       
-       boolean containsKey(Object key); 
-       boolean containsValue(Object value); 
-       
-        V get(Object key); 
-        V put(K key, V value); 
-        V remove(Object key); 
-       
-       void putAll(Map<? extends K, ? extends V> m); 
-       void clear(); 
-       
-        Set<K> keySet(); // key 集合 
-        Collection<V> values(); // value 集合 
-        Set<Map.Entry<K, V>> entrySet(); // key-value 集合 
-       
-       interface Entry<K,V> { 
-              K getKey(); 
-              V getValue(); 
-              V setValue(V value); 
-             
-             boolean equals(Object o); 
-             int hashCode(); 
-        } 
-       
-       boolean equals(Object o); 
-       int hashCode(); 
- } 
public interface Map<K,V> {
       int size();
       boolean isEmpty();
       boolean containsKey(Object key);
       boolean containsValue(Object value);
       V get(Object key);
       V put(K key, V value);
       V remove(Object key);
       void putAll(Map<? extends K, ? extends V> m);
       void clear();
       Set<K> keySet();      // key 集合
       Collection<V> values();     // value 集合
       Set<Map.Entry<K, V>> entrySet();  // key-value 集合
       interface Entry<K,V> {
              K getKey();
              V getValue();
              V setValue(V value);
              boolean equals(Object o);
              int hashCode();
       }
       boolean equals(Object o);
       int hashCode();
}
Hashtable类
Hashtable继承于Dictionary字典,实现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的构造实现:
[java]view plaincopyprint?
- privatetransient Entry[] table; // 数组实现 
- privatefloat loadFactor; 
- public Hashtable(int initialCapacity, float loadFactor) { 
-       if (initialCapacity < 0) 
-       thrownew IllegalArgumentException("Illegal Capacity: " + initialCapacity); 
-       if (loadFactor <= 0 || Float.isNaN(loadFactor)) 
-       thrownew IllegalArgumentException("Illegal Load: " + loadFactor); 
-       
-       if (initialCapacity==0) // 初始容量为0时,默认设置为1 
-        initialCapacity = 1; 
-       this.loadFactor = loadFactor; 
-        table = new Entry[initialCapacity]; 
-        threshold = (int)(initialCapacity * loadFactor); // 实际阈值 
-  } 
- public Hashtable(int initialCapacity) { 
-       this(initialCapacity, 0.75f); // 默认 loadFactor = 0.75f 
-  } 
- public Hashtable() { 
-       this(11, 0.75f); // 默认 initialCapacity = 11, loadFactor = 0.75f 
-  } 
- public Hashtable(Map<? extends K, ? extends V> t) { 
-       this(Math.max(2*t.size(), 11), 0.75f); // 申请两倍Map大小,与默认11比较,取其大 
-        putAll(t); 
-  } 
 private transient Entry[] table; // 数组实现
private float loadFactor;
public Hashtable(int initialCapacity, float loadFactor) {
              if (initialCapacity < 0)
                  throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
              if (loadFactor <= 0 || Float.isNaN(loadFactor))
                  throw new IllegalArgumentException("Illegal Load: " + loadFactor);
              if (initialCapacity==0)  // 初始容量为0时,默认设置为1
                  initialCapacity = 1;
              this.loadFactor = loadFactor;
              table = new Entry[initialCapacity];
              threshold = (int)(initialCapacity * loadFactor); // 实际阈值
}
public Hashtable(int initialCapacity) {
              this(initialCapacity, 0.75f); // 默认 loadFactor = 0.75f
}
public Hashtable() {
this(11, 0.75f);  // 默认 initialCapacity = 11, loadFactor = 0.75f
}
public Hashtable(Map<? extends K, ? extends V> t) {
              this(Math.max(2*t.size(), 11), 0.75f);  // 申请两倍Map大小,与默认11比较,取其大
              putAll(t);
}
使用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是同步的(函数体由synchronized修饰)。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。
但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
HashMap的构造实现:
[java]view plaincopyprint
该贴被zhou编辑于2012-11-26 11:17:40