Java集合框架
一、集合框架(Collections Framework)
这里说的集合就是Java中用于存储对象的容器,不同于数组,这些容器一般是可变长度的,且只能存储对象不能存储基本数据元素,而数组可以。另外,很多容器底部都是基于数组实现的。一般框架为:
二、Collection<E>接口
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
常用方法
- boolean add(E e):向列表尾部添加元素e。
- void add(int index,E e):在列表指定位置插入元素e。
- boolean addAll(Collection<? extends E> c):添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
- boolean addAll(int index,Collection<? extends E> c):在指定位置插入collection中的所有元素。
- void clear():移除所有元素。
- iterator(E) iterator:返回对元素进行迭代的迭代器。
- E get(int index):返回列表中指定位置的元素。
- E remover(int index):移除列表中知道位置的元素,并返回。
- boolean removeAll(Collection<? extends E> c):移除collection中包含的所有元素。
- E set(int index,E e):替换指定位置的元素,并返回被替代的元素。
- int size():返回列表长度。
- Object[] toArray():返回按适当顺序包含列表中所有元素的Object数组。
- T[] toArray(T[] a):返回按适当类型包含列表中所有元素的指定类型T的数组。
三、List<E>接口
1、介绍
有序、可重复、可为null的序列。
List是有序的 collection,我们通常称为序列。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。
常见的List实现类有LinkedList、ArrayList和Vector。
2、LinkedList<E>实现类
不同步的链表结构
ListedList即数据结构中的链表结构。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList
方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list = Collections.synchronizedList(new LinkedList(...));
除了实现List接口的方法,LinkedList自增了一些常用方法:
- E
getFirst()
:返回第一个元素。 E getLast:返回最后一个元素。
- int lastIndexOf(Object o):返回某元素最后出现的索引,没有则返回-1。
- E pop():弹栈。
- void push():压栈。
- E pollFirst():移除并返回第一个元素。
- E pollLast():移除并返回最后一个元素。
3、ArrayList<E>实现类
不同步的可变数组
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。
在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。注意,此实现不是同步的。
4、Vector<E>实现类
同步的可变数组
Vector
类可以实现可增长的对象数组(Vector是向量的意思)。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建 Vector
后进行添加或移除项的操作。
每个向量会试图通过维护 capacity
和 capacityIncrement
来优化存储管理。capacity
始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement
的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。
四、Set<E>接口
1、介绍
不重复且至多一个null的集合
一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2)
的元素对 e1
和 e2
,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
常见实现了有HashSet、TreeSet。
2、HashSet<E>实现类
不同步的无序集合。
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。注意,此实现不是同步的。
3、TreeSet<E>实现类
不同步的可排序集合。
基于 TreeMap
的 NavigableSet
实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator
进行排序,具体取决于使用的构造方法。
此实现为基本操作(add
、remove
和 contains
)提供受保证的 log(n) 时间开销。
注意,如果要正确实现 Set
接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable
或 Comparator
。)这是因为 Set
接口是按照 equals
操作定义的,但 TreeSet
实例使用它的 compareTo
(或 compare
)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了 Set
接口的常规协定。注意,此实现不是同步的。
五、Map<k,v>接口
1、介绍
键值对形式的映射关系集合。
- 类型参数:
K
- 此映射所维护的键的类型
V
- 映射值的类型
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。
Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。
常用方法:
- void clear():清空映射中的所有映射关系。
- boolean containsKey():是否包含指定键的映射关系。
- boolean containsValue():是否有一个或多个映射关系映射到此值。
Set<Map.Entry<K,V>>
entrySet():返回此映射中包含的映射关系的Set
视图。其中,
是映射项(键-值对),是一个整体。Map.Entry<K,V>接口
- Set<K> keySet:返回含此映射中的键Set视图。
- V get(Object key):返回指定键映射的值。
- V put(K key,V value):将指定的值与此映射中的指定键关联(可选操作)。如果已有键则替换为指定新值。
- void
putAll(Map<? extends K,? extends V> m)
:从指定映射中将所有映射关系复制到此映射中。 V remove(Object key):如果存在一个键的映射关系,则将其从此映射中移除。
- int size():返回此映射中的键-值映射关系数。
- Collection<T> values():返回包含此映射中值的Collection视图。
2、HashMap<K,V>实现类
不同步的基于哈希表的map实现
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。注意,此实现不是同步的。
3、TreeMap<K,V>实现类
不同步的基于红黑树的可排序Map实现
基于红黑树(Red-Black tree)的 NavigableMap
实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator
进行排序,具体取决于使用的构造方法。
此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。
注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅 Comparable 或 Comparator)。这是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是 定义良好的,只不过没有遵守 Map 接口的常规协定。注意,此实现不是同步的。
4、LinkedHashMap实现类
不同步的保持插入顺序的链表和哈希表Map实现
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)同样此实现是不同步的。
5、HashTable实现类
哈希表
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null
对象都可以用作键或值。为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode
方法和 equals
方法(因为要保证键唯一,需要比较)。
六、集合的遍历
1、List和set的遍历
- 使用迭代器遍历:Iterator()方法。
1 Iterator iterator = list.iterator(); 2 for(iterator.hasNext()){ 3 int i = (Integer) iterator.next(); 4 System.out.println(i); 5 }
- 使用foreach循环遍历
1 for (Object object : list) { 2 System.out.println(object); 3 }
- 普通循环遍历:get()方法
1 for(int i = 0 ;i<list.size();i++) { 2 int j= (Integer) list.get(i); 3 System.out.println(j); 4 }
2、Map的遍历
- 通过获取所有的key按照key来遍历:keySet方法
1 Set<Integer> set = map.keySet(); //得到所有key的set视图 2 for (Integer in : map.keySet()) { 3 String str = map.get(in);//得到每个key多对用value的值 4 }
- 通过Map.entrySet接口使用iterator遍历key和value:entrySet方法
//方式1 Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); }
//方式2 for (Map.Entry<Integer, String> entry : map.entrySet()) { //Map.entry<Integer,String> 映射项(键-值对) 有几个方法:用上面的名字entry //entry.getKey() ;entry.getValue(); entry.setValue(); //map.entrySet() 返回此映射中包含的映射关系的 Set视图。 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); }
- 通过values()方法遍历所有的value,但不能遍历key
for (String v : map.values()) { System.out.println("value= " + v); }
3、各种遍历方式的比较
存储结构有顺序存储(如ArrayList)和链式存储(如LinkedList)。顺序存储的查询效率高,但链式存储的插入效率高。
遍历方法无非遍历器、for循环、foreach循环三种。传统for循环是基于计数器进行遍历的,而迭代器采用指针的形式逐一遍历,foreach底层也是基于迭代器实现的。
- 1、传统的for循环遍历,基于计数器的:
顺序存储:读取性能比较高。适用于遍历顺序存储集合。
链式存储:时间复杂度太大,不适用于遍历链式存储的集合。
- 2、迭代器遍历,Iterator:
顺序存储:如果不是太在意时间,推荐选择此方式,毕竟代码更加简洁,也防止了Off-By-One的问题。
链式存储:意义就重大了,平均时间复杂度降为O(n),还是挺诱人的,所以推荐此种遍历方式。
- 3、foreach循环遍历:
foreach只是让代码更加简洁了,但是他有一些缺点,就是遍历过程中不能操作数据集合(删除等),所以有些场合不使用。而且它本身就是基于Iterator实现的,但是由于类型转换的问题,所以会比直接使用Iterator慢一点,但是还好,时间复杂度都是一样的。所以怎么选择,参考上面两种方式,做一个折中的选择。
七、使用比较器进行自定义排序
对于TreeSet<E>、TreeMap<K,V>等可排序的容器,有时候需要我们对元素进行自定义排序,一般可通过实现Comparator接口 或 Comparable接口来完成。
1、comparator<T>接口
强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort
或 Arrays.sort
),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set
或有序映射
)的顺序,或者为那些没有自然顺序
的对象 collection 提供排序。
方法:
- int compare(T o1,T o2):比较用来排序的两个参数。
- boolean equals(Object o):指示某个其他对象是否“等于”此 Comparator。
2、comparable<T>接口
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。实现此接口的对象列表(和数组)可以通过 Collections.sort
(和 Arrays.sort
)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
方法:
- int
compareTo(T o)
:比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
八、collections工具类的使用
此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。
常用方法:
- addAll(Collection<? esuper T> c,T...elements):将指定元素添加到集合collection中。
binarySearch(List<? extends Comparable<? super T>> list, T key):使用二分搜索法搜索指定列表,以获得指定对象。
- checkedXXX():返回XXX的一个动态安全视图。
disjoint(Collection<?> c1, Collection<?> c2)
:判断两个集合是否没有相同元素,没有则返回true。- max():根据自然排序或指定比较器排序方式返回最大值。
- min():根据自然排序或指定比较器排序方式返回最小值。
sort(List<T> list)
:根据元素的自然顺序 对指定列表按升序进行排序。sort(List<T> list, Comparator<? super T> c)
:根据指定比较器产生的顺序对指定列表进行排序。