List
概述
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
Collection将集合划分为两大类:
- List集合
- Set集合
List接口的特点
-
有序【存储有序】
-
可重复
-
可以存储
null
值 -
部分子集合线程安全,部分不安全 例如
ArrayList
和Vector
-
有索引,针对每个元素能够方便地查询和修改
-
判断元素是否重复依赖于
equals
方法 a. 如果元素是系统类,不需要重写
equals
方法 b. 如果是自定义类,就需要我们按需求重写
equals
方法
List接口的常用方法
增加
void add(int index, E element) 在指定 index 索引处理插入元素 element
boolean addAll(int index, Collection<? extends E> c) 在指定 index 索引处理插入集合元素 c
删除
E remove(int index) 删除指定索引 index 处的元素
修改
E set(int index, E element) 修改指定索引 index 处的元素为 element
遍历
E get(int index) + int size() for循环遍历集合中的每一个元素
ListIterator<E> listIterator() 通过列表迭代器遍历集合中的每一个元素
ListIterator<E> listIterator(int index) 通过列表迭代器从指定索引处开始正向或者逆向遍历集合中的元素
获取
E get(int index) 获取指定索引处的元素
int indexOf(Object o) 从左往右查找,获取指定元素在集合中的索引,如果元素不存在返回 -1
int lastIndexOf(Object o) 从右往左查找,获取指定元素在集合中的索引,如果元素不存在返回 -1
List<E> subList(int fromIndex, int toIndex) 截取从 fromIndex 开始到 toIndex-1 处的元素
List接口的遍历方式
-
toArray
-
Iterator
-
foreach
-
普通for
-
ListIterator
List接口去除重复元素
方式一:创建一个新的集合去除重复元素再使用地址传递
// 创建新的集合
List<Worker> list2 = new ArrayList<Worker>();
// 遍历旧集合
for (Worker worker : list) {
// 判断新集合是否有该元素
if (!list2.contains(worker)) {
// 如果没有就加进去
list2.add(worker);
}
list = list2;
}
方式二:在原集合的基础上使用选择排序思想去除重复元素
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("李四");
list.add("李四");
list.add("王五");
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) {
list.remove(j);
j--;
}
}
}
并发修改异常的处理
异常名称:并发修改异常 java.util.ConcurrentModificationException
产生原因:在使用迭代器迭代的同时使用原集合对元素做了修改
解决办法:
-
使用 toArray 方法
-
使用 普通 for 遍历
-
使用 ListIterator 遍历集合并且使用 列表迭代器修改元素
***ArrayList
概述
List
接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括null
在内的所有元素。除了实现
List
接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于Vector
类,除了此类是不同步的。)
特点
- 底层数据结构是数组
- 增加和删除的效率低,查询和修改的效率高
- 能够存储 null 值
- 线程不安全,效率高 可以通过 Collections.synchronizedList();变安全
- 有索引,能够方便检索
- 元素可重复,我们自己可以通过 选择排序去重复
- 不可以排序,但是可以通过 Collections.sort();方法排序
注:ArrayList中常用的方法全部来自于 父类 Collection
,List
,Object
.这里不再做详细叙述。
Vector
概述
Vector
类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建Vector
后进行添加或移除项的操作。
特点
- 底层数据结构是数组
- 有索引,能够方便检索
- 增加和删除的效率低,查询和修改的效率高
- 线程安全,效率低
- 能够存储 null 值
- 元素可重复【我们自己可以通过选择排序思想去除重复元素】
- 不可以排序,但是可以通过 Collections.sort();方法排序
常用方法
增加
public synchronized void addElement(E obj) 添加元素 obj 到集合中
public synchronized void insertElementAt(E obj, int index) 在指定索引 index 处插入元素 obj
删除
public synchronized void removeElementAt(int index) 移除指定索引 index 处的元素
public synchronized void removeAllElements() 移除所有元素
修改
public synchronized void setElementAt(E obj, int index) 修改指定索引 index 的元素为 obj
遍历
public synchronized E elementAt(int index) + size() for循环遍历集合中的所有元素
public synchronized Enumeration<E> elements() 使用 Enumeration 迭代器遍历集合中的元素
获取
public synchronized E firstElement() 获取集合中的第一个元素
public synchronized E lastElement() 获取集合中的最后一个元素
public synchronized E elementAt(int index) 获取指定索引 index 的元素
相关面试题
ArrayList和Vector的区别?
1) Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Stack
概述
Stack
类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类Vector
进行了扩展 ,允许将向量视为堆栈。它提供了通常的push
和pop
操作,以及取堆栈顶点的peek
方法、测试堆栈是否为空的empty
方法、在堆栈中查找项并确定到堆栈顶距离的search
方法。
特点
- 基于栈结构的集合,先进后出
Stack
类是Vector
类的子类,所以该类也是线程安全的,效率低,建议使用Deque
接口的实现类
常用方法
E push(E item) 将元素压入栈底
E pop() 将元素从栈结构中弹出,并作为此函数的值返回该对象,此方法会影响栈结构的大小
E peek() 查看堆栈顶部的对象,但不从栈中移除它。
boolean isempty() 测试栈是否为空。
int search(Object o) 返回对象在栈中的位置,以 1 为基数。
注:如果栈中元素为空,再尝试弹栈,将会抛出 EmptyStackException 异常, 而不是 NoSuchElementException
示例代码如下:
Stack<String> stack = new Stack<>();
// 压栈
stack.push("A");
stack.push("B");
stack.push("C");
while (!stack.isEmpty()) {
System.out.println("栈顶元素:" + stack.peek());
// 弹栈
System.out.println("弹出栈顶元素:" + stack.pop());
}
Queue
概述
在处理元素前用于保存元素的 collection。除了基本的
Collection
操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null
或
false
,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的Queue
实现设计的;在大多数实现中,插入操作不会失败。
特点
- 该接口是队列接口的根接口,先进先出
- 该接口提供队列相关两种形式的方法,一种抛出异常(操作失败时),另一种返回一个特殊值(
null
或
false
,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的Queue
实现设计的;在大多数实现中,插入操作不会失败。
常用方法
抛出异常 | 返回特殊值 | |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove() | poll() |
检查 | element() | peek() |
示例代码:
Queue<String> queue = new ArrayDeque<>();
queue.add("A");
queue.add("B");
queue.add("C");
while (!queue.isEmpty()) {
System.out.println(queue.remove());
}
// 或者
queue.offer("A");
queue.offer("B");
queue.offer("C");
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
Deque
概述
一个线性 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数
Deque
实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
特点
Deque
是一个Queue的子接口,是一个双端队列,支持在两端插入和移除元素deque
支持索引值直接存取。Deque
头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时。- 插入、删除、获取操作支持两种形式:快速失败和返回null或true/false
- 不推荐插入null元素,null作为特定返回值表示队列为空
常用方法
第一个元素(头部) | 最后一个元素(尾部) | |||
---|---|---|---|---|
抛出异常 | 特殊值 | 抛出异常 | 特殊值 | |
插入 | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
移除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | peekFirst() | getLast() | peekLast() |
双向队列操作
插入元素
- addFirst(): 向队头插入元素,如果元素为null,则发生空指针异常
- addLast(): 向队尾插入元素,如果为空,则发生空指针异常
- offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false
- offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false
移除元素
- removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException
- removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException
- pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null
- pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null
获取元素
- getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException
- getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException
- peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null
- peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null
栈操作
pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst()
,如果队列无元素,则发生NoSuchElementException
push(): 向栈中压入元素,也就是向队头增加元素,等价于addFirst()
,如果元素为null,则发生NoSuchElementException,如果栈空间受到限制,则发生IllegalStateException
引用场景
- 满足FIFO场景时
- 满足LIFO场景时,曾经在解析XML按标签时使用过栈这种数据结构,但是却选择
Stack
类,如果在进行栈选型时,更推荐使用Deque
类,应为Stack
是线程同步
ArrayDeque
概述
Deque
接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止 null 元素。此类很可能在用作堆栈时快于Stack
,在用作队列时快于LinkedList
。
特点
ArrayDeque
是Deque
接口的一种具体实现,是依赖于可变数组来实现的ArrayDeque
没有容量限制,可根据需求自动进行扩容ArrayDeque
不支持值为 null 的元素。ArrayDeque
可以作为栈来使用,效率要高于 StackArrayDeque
也可以作为队列来使用,效率相较于基于双向链表的 LinkedList 也要更好一些
LinkedList
概述
List
接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括null
)。除了实现
List
接口外,LinkedList
类还为在列表的开头及结尾get
、remove
和insert
元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
特点
LinkedList
底层数据结构是一个双向链表,元素有序,可重复。LinkedList
在实现数据的增加和删除效率高,查询和修改效率低,顺序访问会非常高效,而随机访问效率比较低。LinkedList
实现List
接口,支持使用索引访问元素。LinkedList
实现Deque
接口,即能将LinkedList
当作双端队列使用。LinkedList
是线程不安全的,效率高