Java List集合详解(23大详细用法)

List接口(java.util.List)代表着有序的对象集合, List中包含的元素可以根据它们在List中的内部顺序进行插入、访问、迭代和删除。

List的实现

作为 Collection 的子类,Collection中的所有方法在List中同样实用。既然List是个接口,所有初始化时需要具体的实现,可以选择下面的List的实现:

  • java.util.ArrayList
  • java.util.LinkedList
  • java.util.Vector
  • java.util.Stack

这些实现中, ArrayList应用最广泛。在java.util.concurrent包中也有List的并发类的实现,更多细节后面的文章会讲述。

 

创建List

通过List的实现创建List实例,下面是代码:

List listA = new ArrayList();
List listB = new LinkedList();
List listC = new Vector();
List listD = new Stack();

记住,最常用的是ArrayList,其他的实现我们可以根据具体场景使用。

 

List的泛型

List中默认的是添加Object,但从JAVA5以后增加了泛型,可以让List中添加的元素类型受到限制,下面是代码:

List<MyObject> list = new ArrayList<MyObject>();

List中只能添加 MyObject的实例对象,同时迭代元素时不许强制类型转换,看看下面的代码:

List<MyObject> list = new ArrayList<MyObject>();
 
list.add(new MyObject("First MyObject"));
 
MyObject myObject = list.get(0);
 
for(MyObject anObject : list){
   //do someting to anObject...
}

如果不使用泛型将是下面的样子:

List list = new ArrayList();   //no generic type specified
 
list.add(new MyObject("First MyObject"));
 
MyObject myObject = (MyObject) list.get(0);  //cast needed
 
for(Object anObject : list){
    //cast needed
    MyObject theMyObject = (MyObject) anObject;
 
   //do someting to anObject...
}

注意如何将从列表中检索到的MyObject实例强制转换为MyObject,如果没有设置泛型,编译的时候java只识别Object实例对象,需要强制转换它们的类型。List变量指定泛型是很好得实践,避免了向List中插入错误得类型,能够从List中检索对象,而不必将它们转换为其真实类型, 而且-它帮助代码的读者了解List应该包含什么类型的对象。只有在有充分理由的情况下才应该省略泛型类型。

List中插入元素

可以通过List得add()方法插入元素,下面是代码:

List<String> listA = new ArrayList<>();
 
listA.add("element 1");
listA.add("element 2");
listA.add("element 3");

调用了三次add()方法,增加String到List中。

向指定的索引插入元素

可以将元素插入指定索引位置,List有一个add()方法,第一个参数是索引的位置,第二个参数是元素,下面是代码:

list.add(0, "element 4");

如果List中已经包含元素,那么这些元素现在将在列表的内部序列往后退一个序列,比如在插入新元素前索引是0,然后在0的位置在插入一个元素,则原来的元素索引为1。w element was inserted at index 0, will get pushed to index 1 etc.

 

添加另外一个List的所有元素

可以将一List的所有元素加到另外一个List中,可以使用List的addAll()方法,下面是代码示例:

List<String> listSource = new ArrayList<>();
 
listSource.add("123");
listSource.add("456");
 
List<String> listDest   = new ArrayList<>();
 
listDest.addAll(listSource);

上面例子把listSource 中所有的元素添加到listDest ,

addAll()方法的参数是Collection ,所以可以传入 List 或者Set作为参数,意思就是可以把通过addAll()方法把List或者Set元素加到List中。

 

从List中获取元素

可以通过 List的索引获取元素,可以用get(int index),下面的代码是通过索引访问List的元素:

List<String> listA = new ArrayList<>();
 
listA.add("element 0");
listA.add("element 1");
listA.add("element 2");
 
//access via index
String element0 = listA.get(0);
String element1 = listA.get(1);
String element3 = listA.get(2);

也可以通过迭代按内部顺序访问List的元素,这个后面会讲述。

 

查找List中的元素

可以通过List的下面两个方法查找是否包含元素:

  • indexOf()
  • lastIndexOf()

indexOf() 方法查找的是给定元素在List中元素第一次出现的索引:

List<String> list = new ArrayList<>();
 
String element1 = "element 1";
String element2 = "element 2";
 
list.add(element1);
list.add(element2);
 
int index1 = list.indexOf(element1);
int index2 = list.indexOf(element2);
 
System.out.println("index1 = " + index1);
System.out.println("index2 = " + index2);

输出结果:

index1 = 0
index2 = 1

查找元素List中最后一个位置

lastIndexOf()方法可以查找元素在List中出现的最后一个索引值,下面是代码:

List<String> list = new ArrayList<>();
 
String element1 = "element 1";
String element2 = "element 2";
 
list.add(element1);
list.add(element2);
list.add(element1);
 
int lastIndex = list.lastIndexOf(element1);
System.out.println("lastIndex = " + lastIndex);

输出结果:

lastIndex = 2

元素“element 1”在List中出现了两次,最后一个位置索引是2(第三个元素)。

检查List中是否包含给定的元素

可以通过 List的contains()方法检查List中是否包含给定的元素:

List<String> list = new ArrayList<>();
 
String element1 = "element 1";
 
list.add(element1);
 
boolean containsElement =
    list.contains("element 1");
 
System.out.println(containsElement);

输出结果:

true

因为List中包含”element 1″,为了决定List中是否包含给定的元素,List内部户将要迭代,并且调用equal()方法检查是否与给定的元素相等。既然可以添加null值到List中,那么同样可以检查List中是否包含null值,下面是代码:

list.add(null);
 
containsElement = list.contains(null);
 
System.out.println(containsElement);

显然,如果contains()中传入的是null值,那么contains()内部调用的不是equals()方法而是==。

 

从List中移除元素

可以通过下面两个方法从List中移除元素:

1. remove(Object element)

2. remove(int index)

remove(Object element)移除是List中如果存在的话第一个出现的元素,所有后面的元素前移一个,索引值减1,下面是代码:

List<String> list = new ArrayList<>();
 
String element = "first element";
list.add(element);
 
list.remove(element);

这个例子首先增加了一个元素然后移除,List的 remove(int index)方法是移除给定索引值对应的元素,所有的后面元素往前移动一位,索引值也相应的减1,下面是代码:

List<String> list = new ArrayList<>();
 
list.add("element 0");
list.add("element 1");
list.add("element 2");
 
list.remove(0);

执行完后,List包含 element 1 和 element 2 ,索引值分别是 0 和1。第一个元素 (element 0) 已经被从List中移除。

从List中移除所有元素

List接口包含了clear()方法,这个方法可以移除List中的所有元素。从List中删除所有元素也被称为清除List,下面是代码:

List<String> list = new ArrayList<>();
 
list.add("object 1");
list.add("object 2");
//etc.
 
list.clear();

首先创建List,其次向List中添加两个元素,第三调用clear()方法,调用后List为空。

在List保留给定List中的所有元素

List接口中有个retainAll(),它能够保留一个列表中的所有元素,这些元素也存在于另一个列表中。意思就是,retain()方法移除目标List中在给定的List,中不存在的元素, 就是两个List的交集,下面是代码:

List<String> list      = new ArrayList<>();
List<String> otherList = new ArrayList<>();
 
String element1 = "element 1";
String element2 = "element 2";
String element3 = "element 3";
String element4 = "element 4";
 
list.add(element1);
list.add(element2);
list.add(element3);
 
otherList.add(element1);
otherList.add(element3);
otherList.add(element4);
 
list.retainAll(otherList);

首先创建两个List,然后每个List都新加三个元素,第三步调用list的retainAll()方法参数是otherList , list.retainAll(otherList)执行完后, list中只有 list 和 otherList都有的元素也就是, element1 和element3 。

List大小

可以通过size()获取List的大小,也就是List中元素的个数:

List<String> list = new ArrayList<>();
 
list.add("object 1");
list.add("object 2");
 
int size = list.size();

List的子集

List接口包含一个subList()方法,这个方法可以创建一个原来List的子集。subList()有两个参数:开始索引和结束索引,第一个索引是原List中对应的元素索引,第二个是结束索引,子集中包含起始索引不包括结束索引,和String的substring()非常相似。下面是代码:

List<String> list      = new ArrayList<>();
 
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 4");
 
List<String> sublist = list.subList(1, 3);

执行完list.subList(1,3)后, sublist will contain the 包含索引为1(第二个)和索引为 2(第三个)元素。记住原来List中半酣四个元素,索引是0到3,调用list.subList(1,3)后将包含索引1,但不包括索引3,因此将元素保留在索引1和索引2处。

List 转换成 Set

可以通过创建到一个新的Set,然后调用add方法List作为参数,Set会删除List中的重复元素,只保留一个,下面是代码:

List<String> list      = new ArrayList<>();
 
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
 
Set<String> set = new HashSet<>();
set.addAll(list);

注意 List中element 3添加了两次,Set中包含一次,执行后Set中包含元素 element 1, element 2 和 element 3 。

List转换成数组

可以通过List的 toArray()方法,将List转换成数组:

List<String> list      = new ArrayList<>();
 
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
 
Object[] objects = list.toArray();

也可以将List转换成指定类型得数组,下面是代码:

List<String> list      = new ArrayList<>();
 
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
 
String[] objects1 = list.toArray(new String[0]);

注意,即使我们将大小为0的字符串数组传递给toArray(),返回的数组中也会包含List中的所有元素,它将具有与List相同数量的元素。

数组转换成List

同样可以将数组转换成List,下面是代码:

String[] values = new String[]{ "one", "two", "three" };
 
List<String> list = (List<String>) Arrays.asList(values);

Arrays.asList()方法可以将数组转成List。

 

使用Comparable对List排序

如果List中得元素都是实现了Comparable (java.lang.Comparable)接口,那么他们可以自行比较,看下面得代码:

List<String> list = new ArrayList<>();
 
list.add("c");
list.add("b");
list.add("a");
 
Collections.sort(list);

String类实现了 Comparable接口,可以使用Collections 的sort()方法对他们进行自然排序。

使用Comparator对List排序

如果List中的对象元素没有实现Comparable接口,或者想通过其他方式对它们排序而不是用compare()的实现,那么可以实现Comparator (java.util.Comparator)接口。下面是使用Comparator类对Car类型的List排序:

public class Car{
    public String brand;
    public String numberPlate;
    public int noOfDoors;
 
    public Car(String brand, String numberPlate, int noOfDoors) {
        this.brand = brand;
        this.numberPlate = numberPlate;
        this.noOfDoors = noOfDoors;
    }
}

下面代码是对List中的Car对象排序:

List<Car> list = new ArrayList<>();
 
list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));
 
Comparator<Car> carBrandComparator = new Comparator<Car>() {
    @Override
    public int compare(Car car1, Car car2) {
        return car1.brand.compareTo(car2.brand);
    }
};
 
Collections.sort(list, carBrandComparator);

上面是实现 Comparator的例子,实现只是简单的比较了Car的brand属性,也可以再实现Comparator 比较number plates或者门的数量noOfDoors属性, 同样可以使用Lambda表达式实现Comparator,下面是代码示例:

List<Car> list = new ArrayList<>();
 
list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));
 
 
Comparator<Car> carBrandComparatorLambda      =
    (car1, car2) -> car1.brand.compareTo(car2.brand);
 
Comparator<Car> carNumberPlatComparatorLambda =
    (car1, car2) -> car1.numberPlate.compareTo(car2.numberPlate);
 
Comparator<Car> carNoOfDoorsComparatorLambda  =
    (car1, car2) -> car1.noOfDoors - car2.noOfDoors;
 
Collections.sort(list, carBrandComparatorLambda);
Collections.sort(list, carNumberPlatComparatorLambda);
Collections.sort(list, carNoOfDoorsComparatorLambda);

 

使用Iterator迭代List

第一种方法使用Iterator 迭代List,下面是代码:

List<String> list = new ArrayList<>();
 
list.add("first");
list.add("second");
list.add("third");
 
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    String next = iterator.next();
}

可以调用List的iterator()Iterator,一旦获取了Iterator ,就可以一直调用 hasNext()方法循环直到返回fasle, 调用hasNext()是在while循环中完成的。While内部循环,可以调用Iterator的 next() 方法获取下一个元素。如果List使用了泛型,那么可以在while循环中保存一些对象转换。下面是代码:

List<String> list = new ArrayList<>();
 
list.add("first");
list.add("second");
list.add("third");
    
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
    String obj = iterator.next();
}

使用For-Each循环迭代List

第二种方法For-Each循环是在Java5中引入的,下面是代码:

List list = new ArrayList();
 
list.add("first");
list.add("second");
list.add("third");
 
for(Object element : list) {
    System.out.println(element);
}

for循环对列表中的每个元素执行一次,在for循环中,每个元素依次绑定到obj变量,下面是使用泛型的List迭代:

List<String> list = new ArrayList<String>();
 
//add elements to list
 
for(String element : list) {
    System.out.println(element);
}

注意这人的泛型是String, 因此,可以将for循环中的变量类型设置为String。

使用For循环迭代List

第三种方法是使用标准的for循环迭代:

List list = new ArrayList();
 
list.add("first");
list.add("second");
list.add("third");
    
for(int i=0; i < list.size(); i++) {
    Object element = list.get(i);
}

For循环创建一个int变量,初始值是0,然后循环,直到i的值等于List的大下停止,也就是小于List的大小时一直循环,i的值每次加1,for循环内部可以使用List的get()方法获取元素,下标索引为i。

下面是使用了泛型String:

List<String> list = new ArrayList<String>();
 
list.add("first");
list.add("second");
list.add("third");
    
for(int i=0; i < list.size(); i++) {
    String element = list.get(i);
}

注意此时For循环内部的变量是String,因为List的泛型是String,List中只能包含String对象,因此编译后get()方法返回的是String类型,不需要强制转换。

使用Stream API迭代List

第四种方式是是由Java Stream API迭代List,为了迭代List,需要从List中获取Stream ,可以通过List的 stream()方法获取,下面是代码:

List<String> stringList = new ArrayList<String>();
 
stringList.add("abc");
stringList.add("def");
 
Stream<String> stream = stringList.stream();

最后一行代码调用了List的 stream() 方法,从List中获取了Stream,一旦获取到了Stream,就可以调用Stream的forEach()方法迭代,下面是例子:

List<String> stringList = new ArrayList<String>();
 
stringList.add("one");
stringList.add("two");
stringList.add("three");
 
Stream<String> stream = stringList.stream();
stream
    .forEach( element -> { System.out.println(element); });
 

调用forEach()方法,将迭代Stream 内部的所有元素,Consumer 为流中的每个元素调用作为参数传递给forEach()方法的使用者。

作者简介

陈睿|mikechen,10年+大厂架构经验,大厂资深面试官,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

👇阅读更多mikechen架构文章👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

评论交流
    说说你的看法