Java列表

时间:2020-01-09 10:35:48  来源:igfitidea点击:

Java List接口java.util.List表示对象的有序序列。 Java List中包含的元素可以根据它们在Java List中内部出现的顺序进行插入,访问,迭代和删除。元素的顺序是为什么此数据结构称为列表的原因。

Java"列表"中的每个元素都有一个索引。 "列表"中的第一个元素的索引为0,第二个元素的索引为1,依此类推。索引的意思是"离列表开头还有多少个元素"。因此,第一个元素与列表的开头相距0个元素,因为它位于列表的开头。

我们可以将任何Java对象添加到"列表"中。如果未使用Java泛型键入"列表",那么我们甚至可以在同一"列表"中混合使用不同类型(类)的对象。在实践中,通常不会在同一"列表"中混合不同类型的对象。

JavaList接口是标准的Java接口,它是Java Collection接口的子类型,这意味着List继承自Collection

列表与集合

JavaList和Java Set接口非常相似,因为它们都表示元素的集合。但是,有一些显着差异。这些不同体现在List和Set接口提供的方法上。

Java List和Java Set接口之间的第一个区别是,同一元素在Java List中可以出现多次。这与Java" Set"不同,在Java" Set"中,每个元素只能出现一次。

Java List和Java Set接口之间的第二个区别是List中的元素具有顺序,并且可以按照该顺序进行迭代。 Java Set没有对内部保留的元素的顺序作出任何保证。

列表的实现

作为"集合"子类型,"集合"接口中的所有方法在"列表"接口中也可用。

由于"列表"是一个接口,因此我们需要实例化该接口的具体实现才能使用它。我们可以在Java Collections API的以下"列表"实现之间进行选择:

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

在这些实现中,ArrayList是最常用的。

java.util.concurrent包中还有并发的List实现。这些List实现在我的java.util.concurrent教程中有更详细的解释。

建立列表

我们可以通过创建实现List接口的类之一的实例来创建List实例。以下是一些如何创建List实例的示例:

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

请记住,大多数情况下,我们将使用ArrayList类,但是在某些情况下,使用其他实现之一可能是有意义的。

通用列表

默认情况下,我们可以将任何"对象"放入"列表"中,但是从Java 5开始,Java泛型可以限制我们可以插入"列表"中的对象类型。这是一个例子:

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

现在,该"列表"只能插入" 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。如果没有在List变量声明上设置泛型类型,则Java编译器仅知道List包含Object实例。因此,我们需要将它们强制转换为知道对象所属的具体类(或者接口)。

最好为List变量指定通用类型。它可以避免将错误类型的对象插入到列表中。它使我们能够从列表中检索对象,而不必强制转换它们的真实类型。它可以帮助代码阅读者查看列表应该包含的对象类型。仅在有充分理由的情况下才应省略泛型类型。

在Java列表中插入元素

我们可以使用Java对象的add()方法将元素(对象)插入Java List中。这是使用add()方法向Java List中添加元素的示例:

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

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

前三个add()调用将一个String实例添加到列表的末尾。

插入空值

实际上可以将" null"值插入Java" List"中。这是在JavaList中插入null值的示例:

Object element = null;

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

list.add(element);

在特定索引处插入元素

可以在特定索引处将元素插入Java" List"中。 List接口具有add()方法的版本,该方法将索引作为第一个参数,并将要插入的元素作为第二个参数。这是将索引0处的元素插入Java List中的示例:

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

如果"列表"已经包含元素,则这些元素将按"列表"的内部顺序进一步下移。在将新元素插入索引0之前具有索引0的元素将被推送到索引1等。

将一个列表中的所有元素插入到另一个列表中

可以将一个Java"列表"中的所有元素添加到另一个"列表"中。我们可以使用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或者Java Set作为参数。换句话说,我们可以使用addAll()将"列表"或者"集合"中的所有元素添加到"列表"中。

从Java列表中获取元素

我们可以使用元素的索引从Java"列表"中获取元素。我们可以使用get(int index)方法来实现。这是一个使用元素索引访问JavaList元素的示例:

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

也可以按照Java" List"元素在内部存储的顺序进行迭代。在本Java List教程的后面,我将向我们展示如何做到这一点。

在列表中查找元素

我们可以使用以下两种方法之一在JavaList中找到元素:

  • indexOf()
  • lastIndexOf()

indexOf()方法在给定元素的List中找到第一次出现的索引。这是一个在JavaList中查找两个元素的索引的示例:

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

在列表中查找元素的最后一次出现

lastIndexOf()方法找到给定元素的List中最后一次出现的索引。这是一个示例,显示了如何在JavaList中查找给定元素的最后一次出现的索引:

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

运行上述Java示例输出的输出将是:

lastIndex = 2

元素"元素1"在"列表"中出现2次。最后一次出现的索引是2.

检查列表是否包含元素

我们可以使用List``contains()方法检查JavaList是否包含给定的元素。这是一个使用contains()方法检查JavaList是否包含元素的示例:

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

String element1 = "element 1";

list.add(element1);

boolean containsElement =
    list.contains("element 1");

System.out.println(containsElement);

运行此JavaList示例的输出将是:

true

...因为List实际上包含了元素。

为了确定List是否包含元素,List将在内部对其元素进行迭代,并将每个元素与作为参数传递的对象进行比较。比较使用元素的Java equals方法检查元素是否等于参数。

由于可以将" null"值添加到" List",因此实际上可以检查" List"是否包含" null"值。这是检查"列表"是否包含"空"值的方法:

list.add(null);

containsElement = list.contains(null);

System.out.println(containsElement);

显然,如果contains()的输入参数为null,那么contains()方法将不使用equals()方法与每个元素进行比较,而是使用==运算符。

从Java列表中删除元素

我们可以通过以下两种方法从Java"列表"中删除元素:

  • remove(Object element)
  • remove(int index)

remove(Object element)删除列表中的那个元素(如果存在的话)。然后,列表中的所有后续元素将在列表中上移。因此,它们的索引减少1. 这是一个基于元素本身从Java" List"中删除元素的示例:

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

String element = "first element";
list.add(element);

list.remove(element);

此示例首先将元素添加到"列表",然后再次将其删除。

Listremove(int index)方法删除给定索引处的元素。然后,列表中的所有后续元素将在列表中上移。因此,它们的索引减少了1. 这是一个按其索引从JavaList中移除元素的示例:

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

list.add("element 0");
list.add("element 1");
list.add("element 2");

list.remove(0);

运行此Java示例代码后,"列表"将在索引0和1处包含Java字符串元素"元素1"和"元素2"。第一个元素("元素0")已从"列表"中删除。

从Java列表中删除所有元素

JavaList接口包含一个clear()方法,该方法在被调用时从列表中删除所有元素。从"列表"中删除所有元素也称为清除"列表"。这是一个简单的示例,该示例使用clear()方法从List(清除)中删除所有元素:

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

list.add("object 1");
list.add("object 2");
//etc.

list.clear();

首先,创建一个新的"列表"。其次,将两个元素添加到"列表"中。第三,调用clear()方法。调用clear()方法后,List将完全为空。

将一个列表中的所有元素保留在另一个列表中

Java" List"接口具有一种名为" retainAll()"的方法,该方法能够保留一个" List"中的所有元素,这些元素也存在于另一个" List"中。换句话说," retain()"方法从目标"列表"中删除所有在另一个"列表"中找不到的元素。产生的"列表"是两个列表的交集。这是调用List``retainAll()方法的Java示例:

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

创建前两个列表。其次,将3个元素添加到"列表"中,并将3个元素添加到" otherList"中。第三,在" list"上调用" retainAll()"方法,并传递" otherList"作为参数。在list.retainAll(otherList)完成执行之后,list将只包含在调用retainAll()之前存在于list和otherList中的那些元素。更具体地说,就是element1element3

列表大小

我们可以通过调用size()方法来获取List中元素的数量。这是一个例子:

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

list.add("object 1");
list.add("object 2");

int size = list.size();

列表子列表

Java" List"接口有一个名为" subList()"的方法,该方法可以创建一个新的" List",并使用原始" List"中元素的子集。

subList()方法采用2个参数:开始索引和结束索引。起始索引是原始"列表"中要包括在子列表中的第一个元素的索引。结束索引是子列表的最后一个索引,但是最后一个索引处的元素不包括在子列表中。这类似于Java String子字符串方法的工作方式。

这是一个Java示例,该示例使用subList()方法从另一个List中创建元素的子列表:

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将包含索引1和2的元素。请记住,原始列表有4个索引从0到3的元素。list.subList(1 ,3)调用将包括索引1,但不包括索引3,从而将元素保持在索引1和2.

将列表转换为集合

我们可以通过创建新的Set并将Java List中的所有元素添加到Java List中来将其转换为Java Set中的内容。 Set将删除所有重复项。因此,生成的"集合"将包含"列表"中的所有元素,但仅包含一次。这是将Java List转换为Set的示例:

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

注意,"列表"两次包含字符串"元素3"。 Set将只包含一次该字符串。因此,结果集将包含字符串"元素1","元素2"和"元素3"。

将列表转换为数组

我们可以使用ListtoArray()方法将Java List转换为Java Array。这是将JavaList转换为Java数组的示例:

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();

也可以将"列表"转换为特定类型的数组。这是将JavaList转换为特定类型的数组的示例:

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的String数组传递给toArray(),返回的数组也将具有List中的所有元素。它将具有与"列表"相同数量的元素。

将数组转换为列表

也有可能将JavaList转换为数组。这是一个将Java数组转换为List的示例:

String[] values = new String[]{ "one", "two", "three" };

List<String> list = (List<String>) Arrays.asList(values);

是将Arrays.asList()方法转换为List的方法。

排序列表

我们可以使用Collections sort()方法对Java List进行排序。我已经在我的Java排序排序教程中解释了它的工作方式,但是在以下几节中,我将仅向我们展示几种对Java"列表"进行排序的方法。

可比较对象的排序列表

如果"列表"包含实现"可比较"接口的对象(" java.lang.Comparable"),则这些对象可以相互比较。在这种情况下,我们可以像这样对List进行排序:

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

list.add("c");
list.add("b");
list.add("a");

Collections.sort(list);

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

使用比较器对列表进行排序

如果JavaList中的对象未实现Comparable接口,或者我们想以不同于对象compare()实现的其他顺序对对象进行排序,则需要使用Comparator实现( java.util.Comparator)。这是一个使用"比较器"对"汽车"对象列表进行排序的示例。首先是"汽车"课程:

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;
    }
}

以下是对上述"汽车"对象的Java"列表"进行排序的代码:

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

list.add(new Car("Volvo V40" , "XYZ 201645", 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实现。此实现仅比较"汽车"对象的"品牌"字段。可以创建另一个"比较器"实现,以比较车号牌,甚至是车门的数量。

还要注意,可以使用Java Lambda实现"比较器"。这是一个示例,使用" Comparator"接口的三个不同的Java lambda实现对" Car"对象的" List"进行排序,每个实现都通过不同的字段比较" Car"实例:

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

list.add(new Car("Volvo V40" , "XYZ 201645", 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);

迭代列表

我们可以通过几种不同的方式来迭代Java" List"。三种最常见的方式是:

  • 使用`迭代器
  • 使用for-each循环
  • 使用for循环
  • 使用Java Stream API

在以下各节中,我将解释每种迭代JavaList的方法。

使用迭代器迭代列表

迭代"列表"的第一种方法是使用Java迭代器。这是一个用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()方法,直到返回false。如我们所见,调用hasNext()是在while循环内完成的。

while循环内,我们调用Iterator接口的Iterator``next()方法来获取Iterator指向的下一个元素。

如果使用Java泛型键入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循环迭代列表

迭代"列表"的第二种方法是使用Java 5中添加的" for"循环(也称为" for each"循环)。这是一个使用for循环迭代List的例子:

List list = new ArrayList();

list.add("first");
list.add("second");
list.add("third");

for(Object element : list) {
    System.out.println(element);
}

for循环对于List中的每个元素执行一次。在for循环中,每个元素又绑定到obj变量。

如果列表是键入的(通用的"列表"),则可以在" for"循环内更改变量的类型。这是键入" List"的迭代示例:

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

//add elements to list

for(String element : list) {
    System.out.println(element);
}

注意如何将"列表"键入为"字符串"。因此,我们可以将" for"循环内的变量类型设置为" String"。

使用For循环迭代列表

迭代"列表"的第三种方法是使用标准的" 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。然后,只要int变量i小于列表的大小,它就会循环。对于每次迭代,变量" i"都会增加。

在" for"循环中,该示例通过其get()方法访问" List"中的元素,并将递增变量" i"作为参数传递。

同样,如果使用Java泛型将List键入为例如到"字符串",则可以使用"列表"的通用类型作为局部变量的类型,该局部变量在迭代过程中被分配给"列表"中的每个元素。一个示例将使这一点更加清楚:

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。由于"列表"的类型通常为"字符串",因此它只能包含"字符串"对象。因此,编译器知道从get()方法只能返回一个String。因此,我们无需将get()返回的元素强制转换为String。

使用Java Stream API迭代列表

迭代Java"列表"的第四种方法是通过Java Stream API。要迭代Java"列表",我们必须首先从"列表"中获取"流"。从Java中的"列表"中获取"流"是通过调用"列表" stream()方法来完成的。这是一个从Java List中获取Java Stream的示例:

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

stringList.add("abc");
stringList.add("def");

Stream<String> stream = stringList.stream();

这是该示例的最后一行,它调用List方法的stream()方法来获取代表List中的元素的Stream方法。

一旦从"列表"中获取了"流",就可以通过调用其" forEach()"方法来对"流"进行迭代。这是一个使用Stream``forEach()方法迭代List元素的例子:

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在内部迭代Stream的所有元素,并为Stream中的每个元素调用作为参数传递给forEach()方法的Consumer。 。