背景
容器是Java的重要组成部分,在实际应用中选择适当的容器,往往能达到事半功倍的效果。
下图为Java集合框架图,图源于菜鸟教程:
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
从上图中也可以看到,Java的类基本上都继承自Collection和Map。我们平时使用的主要是一些具体实现类,例如ArrayList,LinkedList,HashSet,HashMap等。本文将对这些容器的基础操作进行汇总,以便复习与查阅。
NOTE: 初始化容器时需要注意抽象类(或接口)和具体实现类的区别。抽象类只关心抽象方法的定义,不关心具体如何实现。抽象类不可以实例化对象,也就是说new后面不能接抽象类,而要接具体实现类。否则会引发错误XXX is abstract; cannot be instantiated
。
二、各类容器
2.1 List
List是一种接口。使用前需要导入java.util.List相关包。
具体实现类有:
- ArrayList
- LinkedList
- Vector
- Stack:Vector的子类
ArrayList和LinkedList常放在一起比较,两者都是线程不安全的。ArrayList的底层是Object数组,LInkedList的底层结构是双向链表。所以两者本质的区别类似于数组与链表的区别(读取、删除等操作有不同)。
Vector的底层同样是Object数组,是线程安全的,但当前大部分情况都使用ArrayList。
初始化方法
//List 的ArrayList 实现
List<String> list1 = new ArrayList<>();
// List 的LinkedList 实现
List<String> list2 = new LinkedList<>();
// Stack对象创建
Stack<Integer> stk = new Stack<>();
NOTE:尖括号里不能用基础数据类型,需要包装。
使用方法
- ArrayList
// 添加元素
list.add(obj);
list.addAll();
// boolean addAll(Collection c);
// 访问元素,按索引
obj = list.get(index);
// 删除元素,按索引
list.remove(index);
// 删除元素,按内容
list.remove(obj);
// 计算大小
len = list.size()
// 返回元素的索引值
index = list.indexOf(obj);
// 判断是否包含某元素
list.contains();
// 清空
list.clear();
// 判断是否为空
list.isEmpty();
- LinkedList
ArrayList列举的方法LinkedList都可以用,除此外还有:
public void addFirst(E e) // 元素添加到头部。
public void addLast(E e) // 元素添加到尾部。
public E removeFirst() // 删除并返回第一个元素。
public E removeLast() // 删除并返回最后一个元素。
- Stack
// 压栈
stk.push(Object element);
// 弹栈并返回元素
obj = stk.pop();
// 获取顶上元素
obj = stk.peek();
// 判空
stk.isEmpty();
stk.empty();
2.2 Set
Java中常用的Set为HashSet,和List相比较,Set是不允许有重复元素的,每个元素都具有独一无二的值。
另外,HashSet允许存在null值;无序;非线程安全。
初始化方法
HashSet<String> sites = new HashSet<String>();
使用方法
添加、删除、判空、清空都和ArrayList相同。
sites.add(obj); // 添加
sites.remove(obj); // 删除
sites.size(); // 返回大小
sites.isEmpty(); // 判空
sites.contains(obj); // 判断是否包含元素
sites.clear(); // 清空
2.3 Queue
Queue是Java的队列,队列与栈相反,属于“先进先出”的数据表。Queue是接口,不能直接使用。实现Queue接口的类有:
- LinkedList
- ArrayDeque
- PriorityQueue
PriorityQueue比较特殊,属于优先级队列,可以实现大顶堆或小顶堆。默认是小顶堆。但是可以通过自定义比较器实现其他的需求。
以上几种实现类均非线程安全的。Queue还有一些阻塞的实现类,暂时不在本文记录。
初始化方法
// 使用 LinkedList 创建
Queue<String> animal1 = new LinkedList<>();
// 使用 ArrayDeque 创建
Queue<String> animal2 = new ArrayDeque<>();
// 使用 PriorityQueue创建
Queue<String> animal3 = new PriorityQueue<>();
PriorityQueue<Integer> numbers = new PriorityQueue<>();
使用方法
- LinkedList
// 添加
q.add(obj); // 失败抛异常
q.offer(obj); // 失败返回false
// 返回并删除队首元素
q.remove();
q.poll(); // 失败返回false
// 获取队首元素
q.peek();
// 判空
q.isEmpty();
// 大小
q.size();
- PriorityQueue
// 添加
p.offer(obj);
// 返回并删除队首元素
p.poll();
// 获取队首元素
p.peek();
// 如果想要修改默认的小顶堆设置,改为大顶堆,需要重写Comparator的compare方法
// 重写
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 用lambda语法,更简洁
PriorityQueue<Integer> pq = new PriorityQueue<>((x, y) -> y - x);
2.4 Map
Map的实现类常见的有HashMap。
初始化方法
HashMap<Key, Value> numbers = new HashMap<>();
使用方法
// 加入键值对
m.put(key, value);
// 按key获取value
v = m.get(key);
// 返回所有key集合
m.keySet();
// 返回所有value集合
m.values();
// 删除
m.remove(key);
m.remove(key, value);
// 替换
m.replace(key, value);
m.replace(key, oldValue, newValue);
// 判断是否包含key
m.containsKey(key);
// 判断是否包含value
m.containsValue(value);
参考: