Java列表
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);
此示例首先将元素添加到"列表",然后再次将其删除。
List
remove(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中的那些元素。更具体地说,就是element1
和element3
。
列表大小
我们可以通过调用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
。 。