Java集合大致可分为Set、List和Map三种体系,其中Set代表无序、不可重复的集合;List代表有序、重复的集合;而Map则代表具有映射关系的集合。Java 5之后,增加了Queue体系集合,代表一种队列集合实现。
Java集合框架主要由Collection和Map两个根接口及其子接口、实现类组成。本文仅探讨Collection接口及其子接口、实现类。
目录
1. Collection接口继承树
2. Collection接口基本操作
3. Collection的遍历
4. Set子接口
4.1 HashSet类
4.2 LinkedHashSet类
4.3 SortedSet接口及TreeSet实现类
4.4 EnumSet类
5. List子接口
5.1 ArrayList和Vector实现类
5.2 LinkedList类
6. Queue子接口
6.1 PriorityQueue类
6.2 Deque子接口与ArrayDeque类
6.3 实现List接口与Deque接口的LinkedList类
7. 各种线性表选择策略
1. Collection接口继承树
2. Collection接口是Set、List和Queue接口的父接口,基本操作包括:
- add(Object o):增加元素
- addAll(Collection c):...
- clear():...
- contains(Object o):是否包含指定元素
- containsAll(Collection c):是否包含集合c中的所有元素
- iterator():返回Iterator对象,用于遍历集合中的元素
- remove(Object o):移除元素
- removeAll(Collection c):相当于减集合c
- retainAll(Collection c):相当于求与c的交集
- size():返回元素个数
- toArray():把集合转换为一个数组
3. Collection的遍历可以使用Iterator接口或者是foreach循环来实现
参考:Java:集合,数组(Array)、集合(List/Set/Queue)、映射(Map)等的迭代遍历,比如:ArrayList,LinkedList,HashSet,HashMap
4. Set子接口
Set集合不允许包含相同的元素,而判断两个对象是否相同则是根据equals方法。
4.1 HashSet类
HashSet类是Set接口的典型实现类。特点:
- 不能保证元素的排列顺序,加入的元素要特别注意hashCode()方法的实现。
- HashSet不是同步的,多线程访问同一步HashSet对象时,需要手工同步。
- 集合元素值可以是null。
4.2 LinkedHashSet类
LinkedHashSet类也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序。与HashSet相比,特点:
- 对集合迭代时,按增加顺序返回元素。
- 性能略低于HashSet,因为需要维护元素的插入顺序。但迭代访问元素时会有好性能,因为它采用链表维护内部顺序。
4.3 SortedSet接口及TreeSet实现类
TreeSet类是SortedSet接口的实现类。因为需要排序,所以性能肯定差于HashSet。与HashSet相比,额外增加的方法有:
- first():返回第一个元素
- last():返回最后一个元素
- lower(Object o):返回指定元素之前的元素
- higher(Obect o):返回指定元素之后的元素
- subSet(fromElement, toElement):返回子集合
可以定义比较器(Comparator)来实现自定义的排序。默认自然升序排序。
4.4 EnumSet类
EnumSet类是专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值。《Effective Java》第32条,用EnumSet代替位域,示范:
// EnumSet - a modern replacement for bit fields - Page 160 import java.util.*; public class Text { public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } // Any Set could be passed in, but EnumSet is clearly best public void applyStyles(Set<Style> styles) { // Body goes here } // Sample use public static void main(String[] args) { Text text = new Text(); text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); } }
5. List子接口
List子接口是有序集合,所以与Set相比,增加了与索引位置相关的操作:
- add(int index, Object o):在指定位置插入元素
- addAll(int index, Collection c):...
- get(int index):取得指定位置元素
- indexOf(Obejct o):返回对象o在集合中第一次出现的位置
- lastIndexOf(Object o):...
- remove(int index):删除并返回指定位置的元素
- set(int index, Object o):替换指定位置元素
- subList(int fromIndex, int endIndex):返回子集合
5.1 ArrayList和Vector实现类
- 这两个类都是基于数组实现的List类。
- ArrayList是线程不安全的,而Vector是线程安全的。但Vector的性能会比ArrayList低,且考虑到兼容性的原因,有很多重复方法。
- Vector提供一个子类Stack,可以挺方便的模拟“栈”这种数据结构(LIFO,后进先出)。
结论:不推荐使用Vector类,即使需要考虑同步,即也可以通过其它方法实现。同样我们也可以通过ArrayDeque类或LinkedList类实现“栈”的相关功能。所以Vector与子类Stack,建议放进历史吧。
5.2 LinkedList类
不像ArrayList是基于数组实现的线性表,LinkedList类是基于链表实现的。
另外还有固定长度的List:Arrays工具类的方法asList(Object... a)可以将数组转换为List集合,它是Arrays内部类ArrayList的实例,特点是不可以增加元素,也不可以删除元素。
6. Queue子接口
Queue用于模拟队列这种数据结构,实现“FIFO”等数据结构。通常,队列不允许随机访问队列中的元素。
Queue 接口并未定义阻塞队列的方法,而这在并发编程中是很常见的。BlockingQueue 接口定义了那些等待元素出现或等待队列中有可用空间的方法,这些方法扩展了此接口。
Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。
基本操作:
- boolean add(E e) : 将元素加入到队尾,不建议使用
- boolean offer(E e): 将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。推荐使用此方法取代add
- E remove(): 获取头部元素并且删除元素,不建议使用
- E poll(): 获取头部元素并且删除元素,队列为空返回null;推荐使用此方法取代remove
- E element(): 获取但是不移除此队列的头
- E peek(): 获取队列头部元素却不删除元素,队列为空返回null
@Test public void testQueue() { Queue<String> queue = new LinkedList<String>(); queue.offer("1.你在哪儿?"); queue.offer("2.我在这里。"); queue.offer("3.那你又在哪儿呢?"); String str = null; while ((str = queue.poll()) != null) { System.out.println(str); } }
PriorityQueue保存队列元素的顺序并不是按照加入队列的顺序,而是按队列元素的大小重新排序。当调用peek()或者是poll()方法时,返回的是队列中最小的元素。当然你可以与TreeSet一样,可以自定义排序。自定义排序的一个示范:
@Test public void testPriorityQueue() { PriorityQueue<Integer> pq = new PriorityQueue<Integer>(20, new Comparator<Integer>() { public int compare(Integer i, Integer j) { // 对数字进行奇偶分类,然后比较返回;偶数有较低的返回值(对2取余数然后相减),奇数直接相减。 int result = i % 2 - j % 2; if (result == 0) result = i - j; return result; } }); // 倒序插入测试数据 for (int i = 0; i < 20; i++) { pq.offer(20 - i); } // 打印结果,偶数因为有较低的值,所以排在前面 for (int i = 0; i < 20; i++) { System.out.println(pq.poll()); } }
输出:
2,4,6,8,10,12,14,16,18,20,1,3,5,7,9,11,13,15,17,19,
6.2 Deque子接口与ArrayDeque类
Deque代表一个双端队列,可以当作一个双端队列使用,也可以当作“栈”来使用,因为它包含出栈pop()与入栈push()方法。
ArrayDeque类为Deque的实现类,数组方式实现。方法有:
- addFirst(Object o):元素增加至队列开头
- addLast(Object o):元素增加至队列末尾
- poolFirst():获取并删除队列第一个元素,队列为空返回null
- poolLast():获取并删除队列最后一个元素,队列为空返回null
- pop():“栈”方法,出栈,相当于removeFirst()
- push(Object o):“栈”方法,入栈,相当于addFirst()
- removeFirst():获取并删除队列第一个元素
- removeLast():获取并删除队列最后一个元素
6.3 实现List接口与Deque接口的LinkedList类
LinkedList类是List接口的实现类,同时它也实现了Deque接口。因此它也可以当做一个双端队列来用,也可以当作“栈”来使用。并且,它是以链表的形式来实现的,这样的结果是它的随机访问集合中的元素时性能较差,但插入与删除操作性能非常出色。
7. 各种线性表选择策略
- 数组:是以一段连续内存保存数据的;随机访问是最快的,但不支持插入、删除、迭代等操作。
- ArrayList与ArrayDeque:以数组实现;随机访问速度还行,插入、删除、迭代操作速度一般;线程不安全。
- Vector:以数组实现;随机访问速度一般,插入、删除、迭代速度不太好;线程安全的。
- LinkedList:以链表实现;随机访问速度不太好,插入、删除、迭代速度非常快。