1. Overview
1.概述
List is a pretty commonly used data structure in Java. Sometimes, we may need a nested List structure for some requirements, such as List<List<T>>.
List是Java中相当常用的一种数据结构。有时,我们可能需要一个嵌套的List结构来满足某些要求,例如List<List<T>>。
In this tutorial, we’ll take a closer look at this “List of Lists” data structure and explore some everyday operations.
在本教程中,我们将仔细研究这个 “List of Lists “数据结构,并探索一些日常操作。
2. List Array vs. List of Lists
2.列表阵列vs.列表的列表
We can look at the “List of Lists” data structure as a two-dimensional matrix. So, if we want to group a number of List<T> objects, we have two options:
我们可以把 “List of Lists “数据结构看成一个二维矩阵。因此,如果我们想将一些List<T>对象分组,我们有两个选择。
- Array-based: List<T>[]
- List-based: List<List<T>>
Next, let’s have a look at when to choose which one.
接下来,让我们来看看什么时候选择哪一个。
Array is fast for “get” and “set” operations, which run in O(1) time. However, since the array’s length is fixed, it’s costly to resize an array for inserting or deleting elements.
Array对于”get“和”set“操作来说是很快的,其运行时间为O(1)。然而,由于数组的长度是固定的,为了插入或删除元素而调整数组的大小是很昂贵的。
On the other hand, List is more flexible on insertion and deletion operations, which run in O(1) time. Generally speaking, List is slower than Array on “get/set” operations. But some List implementations, such as ArrayList, are internally based on arrays. So, usually, the difference between the performance of Array and ArrayList on “get/set” operations is not noticeable.
另一方面,List在插入和删除操作上更加灵活,其运行时间为O(1)时间。一般来说,List在 “get/set “操作上比Array慢。但是一些 List 实现,例如 ArrayList,内部是基于数组的。因此,通常情况下,Array和ArrayList在 “get/set “操作上的性能差异是不明显的。
Therefore, we would pick the List<List<T>> data structure in most cases to gain better flexibility.
因此,我们会在大多数情况下选择List<List<T>>数据结构以获得更好的灵活性。
Of course, if we’re working on a performance-critical application, and we don’t change the size of the first dimension – for instance, we never add or remove inner Lists – we can consider using the List<T>[] structure.
当然,如果我们正在开发一个对性能要求很高的应用程序,并且我们不改变第一个维度的大小–例如,我们从不添加或删除内部Lists–我们可以考虑使用List<T>[]结构。
We’ll mainly discuss List<List<T>> in this tutorial.
我们将在本教程中主要讨论List<List<T>>。
3. Common Operations on List of Lists
3.对列表的常见操作
Now, let’s explore some everyday operations on List<List<T>>.
现在,让我们来探索一些对List<List<T>>的日常操作。
For simplicity, we’ll manipulate the List<List<String>> object and verify the result in unit test methods.
为了简单起见,我们将操作List<List<String>>对象,并在单元测试方法中验证结果。
Further, to see the change straightforwardly, let’s also create a method to print the content of the List of Lists:
此外,为了直截了当地看到这个变化,让我们也创建一个方法来打印List的Lists的内容。
private void printListOfLists(List<List<String>> listOfLists) {
System.out.println("\n List of Lists ");
System.out.println("-------------------------------------");
listOfLists.forEach(innerList -> {
String line = String.join(", ", innerList);
System.out.println(line);
});
}
Next, let’s first initialize a list of lists.
接下来,让我们首先初始化一个列表。
3.1. Initializing a List of Lists
3.1.初始化List of Lists
We’ll import data from a CSV file into a List<List<T>> object. Let’s first look at the CSV file’s content:
我们将从一个CSV文件中导入数据到一个List<List<T>>对象。让我们先看看CSV文件的内容。
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Delete Me, Mercurial, Git, Subversion
Let’s say we name the file as example.csv and put it under the resources/listoflists directory.
假设我们把文件命名为example.csv,并把它放在resources/listoflists目录下。
Next, let’s create a method to read the file and store the data in a List<List<T>> object:
接下来,让我们创建一个方法来读取文件并将数据存储在一个List<List<T>>对象中。
private List<List<String>> getListOfListsFromCsv() throws URISyntaxException, IOException {
List<String> lines = Files.readAllLines(Paths.get(getClass().getResource("/listoflists/example.csv")
.toURI()));
List<List<String>> listOfLists = new ArrayList<>();
lines.forEach(line -> {
List<String> innerList = new ArrayList<>(Arrays.asList(line.split(", ")));
listOfLists.add(innerList);
});
return listOfLists;
}
In the getListOfListsFromCsv method, we first read all lines from the CSV file into a List<String> object. Then, we walk through the lines List and convert each line (String) into List<String>.
在getListOfListsFromCsv方法中,我们首先从CSV文件中读取所有行到一个List<String>对象。然后,我们走过lines List,将每一行(String)转换成List<String>。
Finally, we add every converted List<String> object to listOfLists. Thus, we’ve initialized a list of lists.
最后,我们将每个转换后的List<String>对象添加到listOfLists。这样,我们就初始化了一个列表。
Curious eyes may have detected that we wrap Arrays.asList(..) in a new ArrayList<>(). This is because the Arrays.asList method will create an immutable List. However, we’ll make some changes to the inner lists later. Therefore, we wrap it in a new ArrayList object.
好奇的人可能已经发现,我们将Arrays.asList(..)包裹在一个新的ArrayList<>()中。这是因为Arrays.asList方法将创建一个不可变的List。然而,我们稍后会对内部列表做一些改变。因此,我们将其包裹在一个新的ArrayList对象中。
If the list of lists object is created correctly, we should have three elements, which is the number of lines in the CSV file, in the outer list.
如果列表对象创建正确,我们应该有三个元素,也就是CSV文件中的行数,在外部列表中。
Moreover, each element is an inner list, and each of those should contain four elements. Next, let’s write a unit test method to verify this. Also, we’ll print the initialized list of lists:
此外,每个元素都是一个内列表,每个内列表应该包含四个元素。接下来,让我们写一个单元测试方法来验证这一点。同时,我们将打印出初始化的列表的列表。
List<List<String>> listOfLists = getListOfListsFromCsv();
assertThat(listOfLists).hasSize(3);
assertThat(listOfLists.stream()
.map(List::size)
.collect(Collectors.toSet())).hasSize(1)
.containsExactly(4);
printListOfLists(listOfLists);
If we execute the method, the test passes and produces the output:
如果我们执行该方法,测试通过并产生输出。
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Delete Me, Mercurial, Git, Subversion
Next, let’s so make some changes to the listOfLists object. But, first, let’s see how to apply changes to the outer list.
接下来,让我们对listOfLists对象做一些改变。但是,首先,让我们看看如何对外层列表进行修改。
3.2. Applying Changes to the Outer List
3.2.对外部列表应用更改
If we focus on the outer list, we can ignore the inner list at first. In other words, we can look at List<List<String>> as the regular List<T>.
如果我们把注意力放在外侧的列表上,我们就可以先忽略内侧的列表。换句话说,我们可以把List<List<String>>看成普通的List<T>.。
Thus, it’s not a challenge to change a regular List object. We can call List‘s methods, such as add and remove, to manipulate the data.
因此,改变一个普通的List对象并不是一个难题。我们可以调用List的方法,如add和remove,来操作数据。
Next, let’s add a new element to the outer list:
接下来,让我们给外层列表添加一个新元素。
List<List<String>> listOfLists = getListOfListsFromCsv();
List<String> newList = new ArrayList<>(Arrays.asList("Slack", "Zoom", "Microsoft Teams", "Telegram"));
listOfLists.add(2, newList);
assertThat(listOfLists).hasSize(4);
assertThat(listOfLists.get(2)).containsExactly("Slack", "Zoom", "Microsoft Teams", "Telegram");
printListOfLists(listOfLists);
An element of the outer list is actually a List<String> object. As the method above shows, we create a list of popular online communication utilities. Then, we add the new list to listOfLists in the position with index=2.
外部列表的一个元素实际上是一个List<String>对象。如上面的方法所示,我们创建了一个流行的在线通信工具的列表。然后,我们将新的列表添加到listOfLists的位置,index=2。
Again, after the assertions, we print the content of listOfLists:
同样,在断言之后,我们打印listOfLists的内容。
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS, Delete Me
Kotlin, Delete Me, Java, Python
Slack, Zoom, Microsoft Teams, Telegram
Delete Me, Mercurial, Git, Subversion
3.3. Applying Changes to Inner Lists
3.3.将更改应用于内部列表
Finally, let’s see how to manipulate the inner lists.
最后,让我们看看如何操作内部列表。
Since listOfList is a nested List structure, we need to first navigate to the inner list object we want to change. If we know the index exactly, we can simply use the get method:
由于listOfList是一个嵌套的List结构,我们需要首先导航到我们想要改变的内部列表对象。如果我们确切地知道索引,我们可以简单地使用get方法。
List<String> innerList = listOfLists.get(x);
// innerList.add(), remove() ....
However, if we would like to apply a change to all inner lists, we can pass through the list of lists object via a loop or the Stream API.
然而,如果我们想对所有的内部列表应用一个改变,我们可以通过一个循环或Stream API来传递列表对象的列表。
Next, let’s see an example that removes all “Delete Me” entries from the listOfLists object:
接下来,让我们看一个例子,从listOfLists对象中删除所有”Delete Me“条目。
List<List<String>> listOfLists = getListOfListsFromCsv();
listOfLists.forEach(innerList -> innerList.remove("Delete Me"));
assertThat(listOfLists.stream()
.map(List::size)
.collect(Collectors.toSet())).hasSize(1)
.containsExactly(3);
printListOfLists(listOfLists);
As we’ve seen in the method above, we iterate each inner list via listOfLists.forEach{ … } and use a lambda expression to remove “Delete Me” entries from innerList.
正如我们在上面的方法中看到的,我们通过listOfLists.forEach{ … }迭代每个内层列表,并使用lambda表达式从innerList中移除”Delete Me“条目。
If we execute the test, it passes and produces the following output:
如果我们执行这个测试,它就会通过并产生以下输出。
List of Lists
-------------------------------------
Linux, Microsoft Windows, Mac OS
Kotlin, Java, Python
Mercurial, Git, Subversion
4. Conclusion
4.总结
In this article, we’ve discussed the list of lists data structure.
在这篇文章中,我们已经讨论了列表的数据结构。
Further, we’ve addressed the common operations on the list of lists through examples.
此外,我们还通过实例解决了对列表的常见操作。
As usual, the complete code of this article can be found over on GitHub.
像往常一样,本文的完整代码可以在GitHub上找到over。