Java集合-List

List接口(java.util.List)代表着有序的对象集合, List中包含的元素可以根据它们在List中的内部顺序进行插入、访问、迭代和删除,元素的顺序就是这个数据结构被称为列表的原因。List中的每个元素都有一个索引,第一个元素的索引是0,第二个元素的索引是1。索引的意思是“离List的第一个元素间隔多少个元素”。因为第一个元素在List的开头,所有间隔为0。如果List不是类型化的,使用Java泛型,那么甚至可以在同一个列表中混合不同类型(类)的对象

然而,在时间开发中很少在List中混合不同类型的对象。

List是一个标准的接口,是Collection接口的子类,意味着继承了Collection的特性。

List Set

ListSet非常相似,都代表了一组元素的集合,但是也有一些明显的不一样, 这些差异反映在ListSet接口提供的方法中。

第一个不同是List中可以重复添加相同的元素,而Set中只能添加一次。第二个不同是,List中的元素是有顺序的,可以按顺序迭代,Set不会对内部保存的元素的顺序做出任何承诺。

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中插入元素

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

List<String> listA = new ArrayList<>();

listA.add("element 1");
listA.add("element 2");
listA.add("element 3");

 

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

插入null

实际是有可能向List中插入null值的,下面是代码:

Object element = null;

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

list.add(element);

 

向指定的索引插入元素

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

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

 

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

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

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

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中是否包含给定的元素

可以通过 Listcontains()方法检查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都新加三个元素,第三步调用listretainAll()方法参数是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中半酣四个元素,索引是03,调用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);

 

注意 Listelement 3添加了两次,Set中包含一次,执行后Set中包含元素 element 1element 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

List排序

可以调用 Collections sort()方法给List排序,之前在前面Collections文章中讲到 ,但是下面我会讲述几种方法:

使用ComparableList排序

如果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()方法对他们进行自然排序。

使用ComparatorList排序

如果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);

 

迭代List

可以通过几种不同的方法迭代List:

  • 使用Iterator
  • 使用for-each循环
  • 使用for循环
  • 使用Stream API

使用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();
}

 

可以调用Listiterator()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的值每次加1for循环内部可以使用Listget()方法获取元素,下标索引为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()方法的使用者,更多的Stream内容后续文章会讲解,或者参考Java Stream API Tutorial.

参考:1.http://tutorials.jenkov.com/java-generics/index.html

2.http://tutorials.jenkov.com/java/interfaces.html

3. https://blog.csdn.net/cgsyck/article/details/108317368

4. https://blog.csdn.net/cgsyck/article/details/108276863

5. http://tutorials.jenkov.com/java-generics/index.html

6. http://tutorials.jenkov.com/java-functional-programming/streams.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值