一 List集合概念
1 List集合概念
在 Java 中,List
是一种常用的集合接口,它继承自 Collection
接口,表示一组有序的元素序列。List
接口允许按照插入顺序存储元素,并且允许通过索引(位置)来访问和修改元素。与集合不同,List
中的元素是可以重复的。
2 List 集合特点
- 有序:存和取的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
3 List实现类
- ArrayList:底层是数组,线程不安全的,有序,可重复,有索引(使用场景:想要集合中的元素可以重复)
- LinkedList:底层是双向链表,线程不安全的,有序,可重复,有索引(使用场景:想要集合中的元素可以重复,而且当前的增删操作明显多于查询的)
- Vector:底层是数组,线程不安全的,有序,可重复,有索引
二 实现方式
采用多态的形式
List<Integer> integers=new ArrayList<>();
List<String> strings=new LinkedList<>();
List<Long> longs=new Vector<>();
三 常用方法
1 接口底层代码
首先我们先大概看一遍底层所写的方法,Java8(按顺序)。
import java.util.function.UnaryOperator;
public interface List<E> extends Collection<E> {
// 集合操作
//返回集合中元素的个数
int size();
//判断集合是否为空
boolean isEmpty();
//判断集合是否包含指定元素
boolean contains(Object o);
//内置迭代器
Iterator<E> iterator();
//将集合中的元素转换为一个数组
Object[] toArray();
//将集合中的元素转换为指定类型的数组。
<T> T[] toArray(T[] a);
// 修改操作
//在集合中添加指定的元素
boolean add(E e);
//在集合中移除指定的元素
boolean remove(Object o);
// 批量修改
//判断集合里面是否包含指定集合
boolean containsAll(Collection<?> c);
//将指定集合中的所有元素添加到调用该方法的集合中
boolean addAll(Collection<? extends E> c);
//将指定集合中的所有元素插入到调用该方法的列表中的指定位置
boolean addAll(int index, Collection<? extends E> c);
//从该集合中移除指定集合中的元素(重复元素也会被全部移除)
boolean removeAll(Collection<?> c);
//保留当前集合中与指定集合相交的元素
boolean retainAll(Collection<?> c);
//使用提供的 UnaryOperator(内含替换规则) 对列表中的每个元素进行替换操作
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
//排序
@SuppressWarnings({"unchecked", "rawtypes"})
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
//清除该集合中的所有元素
void clear();
// 比较与哈希
//与指定对象作比较
boolean equals(Object o);
//返回一个哈希值
int hashCode();
// 位置访问操作
//获取指定索引中的元素
E get(int index);
//设置指定位置上的值
E set(int index, E element);
//在指定位置上添加指定元素
void add(int index, E element);
//移除指定位置上的元素
E remove(int index);
// 查询操作
//用于返回指定元素在列表中第一次出现的索引
int indexOf(Object o);
//用于返回指定元素在列表中最后一次出现的索引
int lastIndexOf(Object o);
// 列表迭代器
//定义一个列表迭代器
ListIterator<E> listIterator();
//用于返回一个列表迭代器,该迭代器从列表中的指定位置开始遍历
ListIterator<E> listIterator(int index);
// 视图
//返回一个新的列表,该列表包含了从 fromIndex(包括)到 toIndex(不包括)的元素
List<E> subList(int fromIndex, int toIndex);
//用于返回一个 Spliterator 对象,支持并行迭代操作
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
}
int size(); | 返回集合中元素的个数 |
boolean isEmpty(); | 判断集合是否为空 |
boolean contains(Object o); | 判断集合中是否包含指定元素 |
Iterator<E> iterator(); | 内置迭代器 |
Object[] toArray(); | 将集合中的所有元素转换为一个数组 |
<T> T[] toArray(T[] a); | 将集合中的所有元素转换为一个指定类型的数组 |
boolean add(E e); | 在集合中添加指定元素 |
boolean remove(Object o); | 在集合中移除指定元素 |
boolean containsAll(Collection<?> c); | 判断集合中是否包含指定集合 |
boolean addAll(Collection<? extends E> c); | 将指定集合中的所有元素添加到集合中 |
boolean addAll(int index, Collection<? extends E> c); | 将指定集合中的所有元素添加到集合中的指定位置 |
boolean removeAll(Collection<?> c); | 从集合中移除指定集合中的所有元素 |
boolean retainAll(Collection<?> c); | 保留当前集合中与传入集合相交的元素 |
default void replaceAll(UnaryOperator<E> operator) | 使用提供的UnaryOperator(内含替换规则)对列表中的每个元素进行替换操作 |
default void sort(Comparator<? super E> c) | 根据指定的比较器对列表进行排序 |
void clear(); | 清除该集合中的所有元素 |
boolean equals(Object o); | 与指定对象作比较 |
int hashCode(); | 返回一个哈希值 |
E get(int index); | 获取指定索引中的元素 |
E set(int index, E element); | 设置指定索引中的元素 |
void add(int index, E element); | 在指定位置上添加指定元素 |
E remove(int index); | 移除指定位置上的元素 |
int indexOf(Object o); | 返回指定元素在列表中第一次出现的索引 |
int lastIndexOf(Object o); | 返回指定元素在列表中最后一次出现的索引 |
ListIterator<E> listIterator(); | 内置列表迭代器 |
ListIterator<E> listIterator(int index); | 内置列表迭代器,从列表的指定位置开始遍历 |
List<E> subList(int fromIndex, int toIndex); | 返回一个新的列表,包含了列表中[fromIndex,oIndex)的元素 |
default Spliterator<E> spliterator() | 返回一个 Spliterator 对象,支持并行迭代操作 |
有一些是父接口都有的方法,前面也有讲到过,这里不再过多的赘述。
2 方法的实现
a.boolean add(E e);——添加指定元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
}
}
b.void add(int index, E element);——在指定位置上添加元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
strings.add(4,"!");
System.out.println(strings); //[hello, welcome, to, java, !]
}
}
c.boolean remove(Object o);——移除指定元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
strings.remove("welcome"); //true
strings.remove("!"); //false
System.out.println(strings); //[hello, to, java]
}
}
d.E remove(int index);——移除指定位置上的元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
strings.remove(0); //true
strings.remove(2); //true
System.out.println(strings); //[hello, to]
}
}
e.E get(int index);——获取指定索引的元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
String s1 = strings.get(0);
String s2 = strings.get(3);
System.out.println(s1+","+s2); //hello,java
}
}
f.E set(int index, E element);——设置指定位置上的数值
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
strings.set(3,"Python");
System.out.println(strings); //[hello, welcome, to, Python]
}
}
g.int size();——获取集合的个数
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
System.out.println(strings.size()); //4
}
}
h.void clear();——清空集合中的元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
strings.clear();
System.out.println(strings); //[]
}
}
i.int indexOf(Object o);——获取指定元素第一次出现的位置
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
System.out.println(strings.indexOf("java")); //3
}
}
j.int lastIndexOf(Object o);——获取指定元素最后一次出现的位置
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> strings=new ArrayList<>();
strings.add("hello"); //true
strings.add("welcome"); //true
strings.add("to"); //true
strings.add("java"); //true
strings.add("java"); //true
System.out.println(strings); //[hello, welcome, to, java, java]
System.out.println(strings.lastIndexOf("java")); //4
}
}
3 总结
boolean add(E e); | 添加指定元素 |
void add(int index, E element); | 在指定位置上添加元素 |
boolean remove(Object o); | 移除指定元素 |
E remove(int index); | 移除指定位置上的元素 |
E get(int index); | 获取指定索引的元素 |
E set(int index, E element); | 设置指定位置上的数值 |
int size(); | 获取集合的个数 |
void clear(); | 清空集合中的元素 |
int indexOf(Object o); | 获取指定元素第一次出现的位置 |
int lastIndexOf(Object o); | 获取指定元素最后一次出现的位置 |
四 List集合的五种遍历方式
1 普通for循环
2 增强for循环
3 Lambda表达式
4 迭代器
5 列表迭代器
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo {
/**
* 普通for循环
* @param list
*/
public void method1(List<String> list){
for (int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
/**
* 增强for循环
* @param list
*/
public void method2(List<String> list){
for (String s:list){
System.out.println(s);
}
}
/**
* Lambda表达式
* @param list
*/
public void method3(List<String> list){
list.forEach(s-> System.out.println(s));
}
/**
* 迭代器
* @param list
*/
public void method4(List<String> list){
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
public static void method5(List<String> list){
ListIterator<String> stringListIterator = list.listIterator();
// 与普通迭代器的区别:在遍历的过程中,可以在后面添加元素
while (stringListIterator.hasNext()){
String s=stringListIterator.next();
if (s.equals("hello")){
stringListIterator.add("java");
}
}
}
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("hello");
list.add("!");
method5(list);
System.out.println(list); //[hello, java, !]
}
}
五 List集合的实现类ArrayList
1 概述
- 是List集合的实现类,底层是数组结构实现,查询快,增删慢
- 由于没有添加同步锁synchronized,所以是线程不安全的
2 方法的实现
由于是List集合的实现类,所以实现了List集合的所有方法
3 源码分析
扩容机制:
- 利用无参构造方法创建的集合,在底层创建一个默认长度为0的数组。
- 添加第一个元素时,底层会创建一个新的长度为10的数组。
- 存满时,会创建一个新的数组,为原来数组的1.5倍,将原来数组中的元素复制到新数组中。
- 如果一次添加多个元素,超过1.5倍的容量,则新创建数组的长度以实际为准。
关键要点:
- 数组名字elementData
- 数组长度size。这里size有两层含义:一是元素的个数;二是下一个元素的存入位置
- 添加元素,添加成功后,size++
底层代码:
//1 利用无参构造方法创建的集合,在底层创建一个默认长度为0的数组。
transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//2 添加第一个元素时,底层会创建一个新的长度为10的数组。
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//3 存满时,会创建一个新的数组,为原来数组的1.5倍,将原来数组中的元素复制到新数组中。
//4 如果一次添加多个元素,超过1.5倍的容量,则新创建数组的长度以实际为准。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
测试一下(注:下述示例中获取容量的方式使用了反射,这样的方式是为了演示,实际应用中请勿直接依赖于类的内部实现。)
import java.util.*;
public class ListDemo {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
// 默认初始容量是 0
System.out.println("默认初始容量是:"+getCapacity(arrayList));
System.out.println("Size: " + arrayList.size() + ", Capacity: " + getCapacity(arrayList));
// 模拟一次存入一个数据
for (int i = 0; i < 20; i++) {
arrayList.add("Element " + i);
System.out.println("Size: " + arrayList.size() + ", Capacity: " + getCapacity(arrayList));
}
// 模拟一次存入大量数据
ArrayList<String> list=new ArrayList<>();
for (int i=0;i<20;i++){
list.add(String.valueOf(i));
}
arrayList.addAll(list);
System.out.println("Size: " + arrayList.size() + ", Capacity: " + getCapacity(arrayList));
System.out.println("存入大量数据时容量是:"+getCapacity(arrayList));
}
private static int getCapacity(ArrayList<?> arrayList) {
// 通过反射获取 ArrayList 的容量
try {
java.lang.reflect.Field field = ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
return ((Object[]) field.get(arrayList)).length;
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
return -1;
}
}
}
4 迭代器源码
这里主要列出关键的方法
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
图解:
结论:
在以后如何避免并发修改异常:在使用迭代器或者增强for循环对集合进行遍历操作的时候,不用使用集合的方法对集合进行增加和删除操作。
六 List集合的实现类LinkedList
1 概述
- 是List集合的实现类,底层是双链表实现,查询慢,增删快
- 由于没有添加同步锁synchronized,所以是线程不安全的
2 方法的实现
由于是List集合的实现类,所以实现了List集合的所有方法,并在其基础上新增了特有方法,下面列出几个较常用的方法(事实中也很少用到LinkedList的特有方法)
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
ublic E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
import java.util.*;
public class ListDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);
// public void addFirst(E e) | 在该列表开头插入指定的元素
list.addFirst(0);
System.out.println(list); // [1, 2, 3]
// public void addLast(E e) | 将指定的元素追加到此列表的末尾
list.addLast(4); // [0, 1, 2, 3]
// public E getFirst() | 返回此列表中的第一个元素
System.out.println(list.getFirst()); // 0
// public E getLast() | 返回此列表中的最后一个元素
System.out.println(list.getLast()); // 4
// public E removeFirst() | 从此列表中删除并返回第一个元素
System.out.println(list.removeFirst()); // 0
// public E removeLast() | 从此列表中删除并返回最后一个元素
System.out.println(list.removeLast()); //4
}
}