java 集合框架

JAVA集合框架主要包括5个部分

  1. Collection接口
  2. Map接口
  3. Iterator接口
  4. Comparable和Comparator接口
  5. Collections和Arrays类


一、Collection接口
Collection接口的继承关系如下:
Collection<--List<--Vector
Collection<--List<--ArrayList
Collection<--List<--LinkedList
Collection<--Set<--HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet

List的特点:保证以某种特定插入顺序来维护元素顺序,即保持插入的顺序,另外元素可以重复。
Set的特点:维持它自己的内部排序,随机访问不具有意义。另外元素不可重复。

1、Vector
Vector核心代码

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable  {  
		protected Object[] elementData;  
		/** 
		 * The number of valid components in this {@code Vector} object. 
		 * Components {@code elementData[0]} through 
		 * {@code elementData[elementCount-1]} are the actual items. 
		 * 
		 * @serial 
		 */  
		protected int elementCount;  
		
		protected int capacityIncrement; //可以设定固定增量  
}
Vector底层数据结构是数组,只不过这个数组的容量可以动态扩展。基于数组查找效率高。而且从线程安全的角度来将,Vector是线程安全的。
同步方式时操作方法前加synchronized关键字,所有效率上会由于这个同步的过程受影响。

2、ArrayList
ArrayList核心代码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
	
	private transient Object[] elementData;
	
	public ArrayList(int initialCapacity) {   
		super();   
		if (initialCapacity < 0) //如果参数小于0,则抛出参数不合法异常   
			throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);   
		this.elementData = new Object[initialCapacity];
	}  
}
ArrayList底层数据结构是数组,这个数组也是动态可扩展的。基于数组查找效率高。它不是线程安全的。

3、LinkedList
LinkedList核心代码
private transient Entry<E> header = new Entry<E>(null, null, null);

private static class Entry<E> {   
    E element;   
    Entry<E> next;   
    Entry<E> previous;   
      
    Entry(E element, Entry<E> next, Entry<E> previous) {   
        this.element = element;   
        this.next = next;   
        this.previous = previous;   
    }   
}
LinkedList底层是基于双向链表实现,查找效率比ArrayList低。它不是线程安全的。

4、HashSet
HashSet核心代码
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable  
{  
    static final long serialVersionUID = -5024744406713321676L;  
  
    // 底层使用HashMap来保存HashSet中所有元素。  
    private transient HashMap<E,Object> map;  
      
    // 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。  
    private static final Object PRESENT = new Object();  
  
    /** 
     * 默认的无参构造器,构造一个空的HashSet。 
     *  
     * 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。 
     */  
    public HashSet() {  
    		map = new HashMap<E,Object>();  
    }  
  
    /** 
     * 构造一个包含指定collection中的元素的新set。 
     * 
     * 实际底层使用默认的加载因子0.75和足以包含指定 
     * collection中所有元素的初始容量来创建一个HashMap。 
     * @param c 其中的元素将存放在此set中的collection。 
     */  
    public HashSet(Collection<? extends E> c) {  
    		map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));  
    		addAll(c);  
    }  
}
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。
对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成

5、LinkedHashSet
LinkedHashSet核心代码
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {

    public LinkedHashSet(int initialCapacity, float loadFactor) {
            super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
}
LinkedHashSet具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按照元素的插入次序显示。
LinkedHashSet继承了HashSet,保持元素的插入次序,因为内部使用LinkedHashMap实现,所以能保持元素插入次序。

6、TreeSet
TreeSet核心代码
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    // NavigableMap对象
    private transient NavigableMap<E,Object> m;

    // TreeSet是通过TreeMap实现的,
    // PRESENT是键-值对中的值。
    private static final Object PRESENT = new Object();

    // 不带参数的构造函数。创建一个空的TreeMap
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    // 将TreeMap赋值给 "NavigableMap对象m"
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    // 带比较器的构造函数。
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<E,Object>(comparator));
    }

    // 创建TreeSet,并将集合c中的全部元素都添加到TreeSet中
    public TreeSet(Collection<? extends E> c) {
        this();
        // 将集合c中的元素全部添加到TreeSet中
        addAll(c);
    }

    // 创建TreeSet,并将s中的全部元素都添加到TreeSet中
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
}
TreeSet基于TreeMap,生成一个总是处于排序状态的set,它实现了SortedSet接口,内部以TreeMap来实现
TreeSet是一个有序的集合,它的作用是提供有序的Set集合。它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, java.io.Serializable接口。
TreeSet继承于AbstractSet,所以它是一个Set集合,具有Set的属性和方法。
TreeSet实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
TreeSet实现了Cloneable接口,意味着它能被克隆。
TreeSet实现了java.io.Serializable接口,意味着它支持序列化。
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序或者根据创建TreeSet时提供的Comparator进行排序。这取决于使用的构造方法。
TreeSet为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。另外,TreeSet是非同步的。

二、Map接口
Map接口的继承关系如下:
Map<--AbstractMap<--HashMap
Map<--AbstractMap<--HashMap<--LinkedHashMap
Map<--AbstractMap<--WeakHashMap
Map<--Dictionary<--HashTable
Map<--SortedMap<--TreeMap

1、HashMap
HashMap核心代码
/** 
 * The table, resized as necessary. Length MUST Always be a power of two. 
 */  
transient Entry[] table;  
  
static class Entry<K,V> implements Map.Entry<K,V> {  
    final K key;  
    V value;  
    Entry<K,V> next;  
    final int hash;  
}
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
在HashMap中,可以允许null作为键,且只可以有一个,否则覆盖,但可以有一个或多个值为null。因为当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null,所以HashMap不能由get()方法来判断否存在某个键,而应该用containsKey()方法来判断;而Hashtable不允许null键与null值。
PS:一个等价于取模的方法
static int indexFor(int h, int length) {  
    return h & (length-1);  
}

2、LinkedHashMap
LinkedHashMap核心代码
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
		/**
		* The head of the doubly linked list.
		* 双向链表的头节点
		*/
		private transient Entry<K,V> header;
		
		/**
		* The iteration ordering method for this linked hash map: true
		* for access-order, false for insertion-order.
		* true表示最近最少使用次序,false表示插入顺序
		*/
		private final boolean accessOrder;
}
LinkedHashMap继承自HashMap,同时自身有一个链表,使用链表存储数据,不存在冲突。
LinkedList和LinkedHashMap一样使用一个双向循环链表,但存储的是简单的数据,并不是“键值对”。
所以HashMap和LinkedHashMap是Map,而LinkedList是一个List,这是他们本质的区别。LinkedList和LinkedHashMap都可以维护内容的顺序,但HashMap不维护顺序。

3、WeakHashMap
WeakHashMap核心代码
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
		// 存储数据的Entry数组,长度是2的幂。
	 	// WeakHashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表
		private Entry[] table;
		
		// queue保存的是“已被GC清除”的“弱引用的键”。
	 	// 弱引用和ReferenceQueue 是联合使用的:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
	  private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
}
WeakHashMap 继承于AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。
更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。这个“弱键”的原理大致上就是,通过WeakReference和ReferenceQueue实现的。
WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:

  1. 新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
  2. 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
  3. 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。

和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

4、HashTable
HashTable核心代码

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable {
		// Hashtable保存key-value的数组。
		// Hashtable是采用拉链法实现的,每一个Entry本质上是一个单向链表
		private transient Entry[] table;
		
		// Hashtable中元素的实际数量
		private transient int count;
}
和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。
Hashtable继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable的函数都是同步的,这意味着它是线程安全的。它的key、value都可以为null。此外,Hashtable中的映射不是有序的。

5、TreeMap
TreeMap核心代码
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
		// 用于保持顺序的比较器,如果为空的话使用自然顺保持Key的顺序
		private final Comparator<? super K> comparator;
		// 根节点
		private transient Entry<K,V> root = null;
		// 树中的节点数量
		private transient int size = 0;
		// 多次在集合类中提到了,用于举了结构行的改变次数
		private transient int modCount = 0;
}

static final class Entry<K,V> implements Map.Entry<K,V> {
		// 键值对的“键”
		K key;
		// 键值对的“值”
    V value;
    // 左孩子
    Entry<K,V> left = null;
    // 右孩子
    Entry<K,V> right = null;
    // 父节点
    Entry<K,V> parent;
    // 红黑树的节点表示颜色的属性
    boolean color = BLACK;
    /**
     * 根据给定的键、值、父节点构造一个节点,颜色为默认的黑色
     */
    Entry(K key, V value, Entry<K,V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
    }
    // 获取节点的key
    public K getKey() {
        return key;
    }
    // 获取节点的value
    public V getValue() {
        return value;
    }
    /**
     * 修改并返回当前节点的value
     */
    public V setValue(V value) {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }
    // 判断节点相等的方法(两个节点为同一类型且key值和value值都相等时两个节点相等)
    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
    }
    // 节点的哈希值计算方法
    public int hashCode() {
        int keyHash = (key==null ? 0 : key.hashCode());
        int valueHash = (value==null ? 0 : value.hashCode());
        return keyHash ^ valueHash;
    }
    public String toString() {
        return key + "=" + value;
    }
}
TreeMap基于红黑树(点击查看树、红黑树相关内容)实现。查看“键”或“键值对”时,它们会被排序(次序由Comparable或Comparator决定)。
TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树

三、Iterator接口
Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
每一种集合类返回的Iterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回SetIterator, Tree可能返回TreeIterator,
但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种Iterator,它只需要获得这个 Iterator接口即可。

四、Comparable和Comparator接口
String和Integer等Java内建类实现Comparable接口以提供一定排序方式,但这样只能实现该接口一次。
对于那些没有实现Comparable接口的类、或者自定义的类,您可以通过Comparator接口来定义您自己的比较方式。

1)Comparable接口
在java.lang包中,Comparable接口适用于一个类有自然顺序的时候。假定对象集合是同一类型,该接口允许您把集合排序成自然顺序。
int compareTo(Object o): 比较当前实例对象与对象o,如果位于对象o之前,返回负值,如果两个对象在排序中位置相同,则返回0,如果位于对象o后面,则返回正值

2)Comparator接口
若一个类不能用于实现java.lang.Comparable,或者您不喜欢缺省的Comparable行为并想提供自己的排序顺序(可能多种排序方式),你可以实现Comparator接口,从而定义一个比较器。

int compare(Object o1, Object o2):
对两个对象o1和o2进行比较,如果o1位于o2的前面,则返回负值,如果在排序顺序中认为o1和o2是相同的,返回0,如果o1位于o2的后面,则返回正值

boolean equals(Object obj):
指示对象obj是否和比较器相等

与Comparable相似,0返回值不表示元素相等。一个0返回值只是表示两个对象排在同一位置。由Comparator用户决定如何处理。
如果两个不相等的元素比较的结果为零,您首先应该确信那就是您要的结果,然后记录行为。

五、Collections和Arrays类
Collections和Arrays类提供了静态方法操作Collection元素和数组元素,比如获取List的最大值或最小值,对List进行排序等等。


你可能感兴趣的:(java 集合框架)