该图为java集合框架的类图,其中实线边框为实现类,比如ArrayList、LinkedList、HashSet、TreeSet等。虚线边框的为抽象类,比如AbstractList、AbstractSet、AbstractMap等。点线边框的为接口,比如Iterator、Collection、Map等。
从图中可以看到Java集合框架主要分割成两部分,集合(Collection)和映射(Map)。它们都实现了迭代器(Iterator)接口。
图中Comparable与Comparator都是用于实现集合中元素比较、排序的接口。Collections与Arrays是操作集合或数组的工具类。
下面对图中主要的接口或类进行总结归纳。以便在使用的时候能够选择合适的集合类。
Collection接口是最基本的集合接口,它定义了有关集合操作的普遍方法。Collection接口派生出三个子接口List、Set、Quene。
List接口继承于Collection接口。List最重要的特性是可以保证元素按照规定的顺序排列。List可以存放重复元素。List为Collection添加了大量的方法,以便我们可以在List中部插入和删除元素(当然只推荐LinkedList这样做,稍后会讲)。List的实现类有Vector、Stack、ArrayList、LinkedList。
Vector实现了一个动态数组。它提供了四种构造方法:
public Vector() //该方法系统会自动对向量进行管理 public Vector(int initialcapacity) //生成一个拥有初始容量的向量 public Vector(int initialCapacity, int capacityIncrement)//向量拥有初始容量,并指定了增量大小 public Vector(Collection<? extends E> c)//向量包含集合c中的元素,元素的顺序以迭代器返回的顺序
ArrayList也实现了一个可变大小的数组。它允许所有元素,包括null。由于ArrayList与Vector内部都是使用数组保存数据,因此获取数据或在集合容器末尾添加、删除元素效率会比较快。关于ArrayList与Vector的不同主要在于①Vector的方法都是同步的,是线程安全的,而ArrayList不是。由于线程的同步会影响性能,因此ArrayList会比Vector性能高。②当ArrayList与Vector空间不够,需要扩展的时候,Vector默认扩展一倍,而ArrayList扩展50%。
ArrayList有三种构造方法:
public ArrayList() //返回一个空列表,长度为十 public ArrayList(Collection<? extends E> c) //返回一个列表,包含集合c中的元素,元素顺序以迭代器返回的顺序 public ArrayList(int initialCapacity) //生成一个具有初始容量的列表
LinkedList实现了双向链表的List,LinkedList也允许null元素。链表的每个节点都包含一个指向前一个元素和一个指向后一个元素的指针。因此LinkedList在添加、删除元素的效率上会比ArrayList高。LinkedList提供了操作列表头和尾元素的方法。包括获取第一个和最后一个元素(getFirst()和getLast()),添加(addFirst()和addLast()),删除(removeFirst()和removeLast())。
LinkedList有两种构造方法:
public LinkedList() //返回一个空的链表 public LinkedList(Collection<? extends E> c) // 返回一个链表,包含集合c中的元素,元素的顺序以迭代器返回的顺序
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供了5个额外的方法使得Vector得以被当做堆栈使用。下面列出构造方法及这五个方法。
public Stack() //返回一个空的堆栈 public empty() //测试堆栈是否为空 public peek() //查看堆栈顶部对象,但不从堆栈中移除 public pop() //移除堆栈顶部对象,并作为函数返回值返回 public push(E item) //把项压入堆栈顶部 public search(Object o) //返回对象在堆栈中的位置
set接口并未扩展Collection接口,它具有与Collection完全一样的接口。Set不允许存放重复元素,因此放入Set的元素必须定义equals()方法以确保对象的唯一性。Set接口不维护元素的顺序。Set接口的实现类包括HashSet、LinkedHashSet、TreeSet。
HashSet是通过包装HashMap来实现,HashSet在添加一个值的时候,实际上是将值作为HashMap中的Key来进行保存。HashSet①不保证元素的排列顺序。②它不是同步的。③可以存放null,但只能存放一个。
HashSet的add方法调用的是底层HashMap的put()方法,HashMap的put()方法,会首先判断key值是否存在,存在则修改value值,不存在插入key-value对。HashSet只使用了HashMap的key,所以不必修改value,只是在判断key是否存在后,执行插入或者不插入操作。HashSet判断元素是否存在的方式是通过判断hashCode值是否相同。
HashSet有四个构造器:
public HashSet() //构建一个空的集合,其包装的HashMap具有默认的初始容量16和负载因子0.75。负载因子是HashMap中存储的数据量/HashMap的总容量,当达到该值时,总容量自动扩展1倍。 public HashSet(Collection<? extends E> c) //构建一个新的集合,该集合包含集合c中的元素。 public HashSet(int initialCapacity) // 构建一个具有初始容量initialCapacity的集合。 public HashSet(int initialCapacity, float loadFactor) // 构建一个具有初始容量initialCapacity和负载因子loadFactor的集合。
LinkedHashSet集合是HashSet的子类,它同样是根据元素的hashCode值决定元素的存储位置,但它同时使用链表维护元素的次序。这样就使得集合中的元素是以插入顺序保存,当遍历此集合时,会以元素的添加顺序访问。
LinkedHashSet在遍历Set中全部元素时,性能比HashSet好,但是因为要以链表维护元素次序,所有插入时性能会逊色于HashSet。
LinkedHashSet构造器与HashSet类似:
public LinkedHashSet() //构建一个空的集合,默认容量16,负载因子0.75 public LinkedHashSet(Collection<? extends E> c) //构建一个新的集合,该集合包含集合c中的元素。 public LinkedHashSet(int initialCapacity) // 构建一个具有初始容量initialCapacity的集合。 public LinkedHashSet(int initialCapacity, float loadFactor) // 构建一个具有初始容量initialCapacity和负载因子loadFactor的集合。
TreeSet是SortedSet的唯一实现类。TreeSet是通过包装TreeMap来实现的,而TreeMap的实现是红黑树结构,也就是一棵自平衡的排序二叉树。因此TreeSet可以保证集合元素处于排序状态。
TreeSet是非同步的,因此不能在多线程间共享。运行速度会比HashSet慢。
TreeSet支持两种排序方式,自然排序和自定义排序。自然排序是默认排序方式。①自然排序:基于二叉树的集合保证元素唯一性或排序顺序是根据其元素的compareTo方法的返回值确定,obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。②自定义排序要继承Comparator接口,实现int compare(T o1,To2)方法。
TreeSet提供四种构造方法:
public TreeSet() //构建一个空的树集,其中元素根据自然排序进行排序 public TreeSet(Collection<? extends E> c) //构建一个新的树集,该集合包含集合c中的元素,元素根据自然排序进行排序 public TreeSet(Comparator<? super E> comparator) //构建一个空的树集,其中的元素按指定比较器进行排序 public TreeSet(SortedSet<E> s) //构建一个包含相同元素的树集,并使用相同的排序集合
Map是一种维护键对象和值对象之间映射关系的集合。一个映射不能包含重复的键,且每个键最多只能映射到一个值。Map提供keySet()方法获得key的集合,提供values()方法获得value的集合。entrySet()方法则可以获得映射关系的集合。
它有几个比较重要的实现类:HashMap、Hashtable、LinkedHashMap、TreeMap。
HashMap是基于哈希表的Map接口的非同步实现。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步能力。HashMap允许null值和null键,但是它不维护元素顺序。因为基于哈希表实现,HashMap的查找,插入,删除操作的算法复杂度为O(n)。
HashMap提供四种构造方法:
public HashMap() //构建一个空的HashMap,默认容量16,负载因子0.75 public HashMap(int initialCapacity) //构建一个空的HashMap,初始容量为initialCapacity,负载因子为0.75 public HashMap(int initialCapacity, float loadFactor) //构建一个空的HashMap,初始容量为initialCapacity,负载因子为loadFactor。 public HashMap(Map<? extends K,? extends V> m) //构建一个新的HashMap,其与m具有相同的映射。
Hashtable与HashMap的实现差不多。它们的主要差别有以下几点:
(1) 虽然都实现了Map接口,但Hashtable类继承于Dictionary抽象类,而HashMap类继承于AbstractMap抽象类。
(2) HashMap可以允许存在一个为null的值和任意个为null的value,但Hashtable中key与value值都不能为null。在HashMap中不能通过get()方法来判断HashMap中是否存在某个键,而要用containsKey()方法来判断。
(3) HashTable的方法使同步的,而HashMap的方法使非同步的。但上文已经提到Collections的synchronizedMap方法使HashMap具有同步能力。
(4) HashMap、HashTable都使用了Iterator。但是HashTable还使用了Enumeration方式。
(5) 哈希值使用不同,Hashtable直接使用对象的hashCode。而HashMap重新计算hash值。
(6) Hashtable和HashMap内部实现方式的数组的初始化大小和扩容的方式。Hashtable中hash数组的默认大小是11,增加方式是old*2+1。HashMap中hash数组的默认值大小是16,增加方式是old*2,而且容量是2的倍数。
Hashtable提供了四种构造方法,与HashMap相似,只是默认容量为11。
public Hashtable() //构建一个空的HashMap,默认容量11,负载因子0.75 public Hashtable(int initialCapacity) //构建一个空的HashMap,初始容量为initialCapacity,负载因子为0.75 public Hashtable(int initialCapacity, float loadFactor) //构建一个空的HashMap,初始容量为initialCapacity,负载因子为loadFactor。 public Hashtable(Map<? extends K,? extends V> m) //构建一个新的HashMap,其与m具有相同的映射。
LinkedHashMap继承自HashMap,它的底层使用哈希表与双向链表保存所有元素。因为拥有双向链表的实现,所以它保留了元素插入顺序,它的迭代顺序也是可以预知的。LinkedHashMap遍历时比HashMap慢,但他的插入、删除操作的算法复杂度都是O(n)级的。它通过重写HashMap的方法来实现链接列表的特性。LinkedHashMap允许使用null键和null值。
LinkedHashMap提供了五种构造方法:
public LinkedHashMap() //构建一个空的LinkedHashMap,初始容量为16,负载因子为0.75 public LinkedHashMap(int initialCapacity) //构建一个空的LinkedHashMap,初始容量为initialCapacity,负载因子为0.75 public LinkedHashMap(int initialCapacity, float loadFactor) //构建一个空的LinkedHashMap,初始容量为initialCapacity,负载因子为loadFactor public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) //构建一个空的LinkedHashMap,初始容量为initialCapacity,负载因子为loadFactor,并指定排序模式 public LinkedHashMap(Map<? extends K,? extends V> m) //构建一个新的HashMap,其与m具有相同的映射。
LinkedHashMap 定义了排序模式accessOrder,该属性为boolean型变量,对于访问顺序,为true。对于插入顺序,则为false。构造方法不指定,默认为插入顺序。
TreeMap实现了SortedMap接口。上文TreeSet已经讲到,TreeMap实现了红黑树的数据结构。TreeMap的每个Entry都被当做红黑树的一个节点来对待。由于红黑树在插入节点和查找时都要通过某一规则遍历,所有效率比HashMap差一些。但它的优点在于可以保持元素的排列顺序。TreeMap的排序同样有两种,自然排序和自定义排序。
TreeMap提供了四种构造方法:
public TreeMap() // 构建一个空的TreeMap,排序方式为自然排序 public TreeMap(Comparator<? super K> comparator) // 构建一个空的TreeMap,排序方式按照comparator public TreeMap(Map<? extends K,? extends V> m) // 构建一个新的TreeMap,它包含映射m中的元素,排序方式为自然排序 public TreeMap(SortedMap<K,? extends V> m) // 构建一个新的TreeMap,它包含映射m中的元素,排序方式与m排序方式相同
最后来讲一下Map.Entry。Map中提供了一个方法entrySet(),该方法返回一个包含元素类型是Map.Entry的集合,它保存的是一个键-值对的组合。Map.Entry是Map声明的一个内部接口。此接口为泛型,定义为Entry<K,V>。它提供getKey(),getValue()方法获得键或值。