在 Java 中迭代列表的方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18410035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ways to iterate over a list in Java
提问by iX3
Being somewhat new to the Java language I'm trying to familiarize myself with all the ways (or at least the non-pathological ones) that one might iterate through a list (or perhaps other collections) and the advantages or disadvantages of each.
作为 Java 语言的新手,我试图让自己熟悉遍历列表(或其他集合)的所有方式(或至少是非病态的方式)以及每种方式的优缺点。
Given a List<E> list
object, I know of the following ways to loop through all elements:
给定一个List<E> list
对象,我知道以下遍历所有元素的方法:
Basic forloop(of course, there're equivalent while
/ do while
loops as well)
基本的for循环(当然,也有等效的while
/do while
循环)
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
Note: As @amarseillan pointed out, this form is a poor choice
for iterating over List
s, because the actual implementation of
the get
method may not be as efficient as when using an Iterator
.
For example, LinkedList
implementations must traverse all of
the elements preceding i to get the i-th element.
注意:正如@amarseillan 所指出的,这种形式对于迭代List
s 来说是一个糟糕的选择,因为该get
方法的实际实现可能不如使用Iterator
. 例如,LinkedList
实现必须遍历 i 之前的所有元素以获取第 i 个元素。
In the above example there's no way for the List
implementation to
"save its place" to make future iterations more efficient.
For an ArrayList
it doesn't really matter, because the complexity/cost of get
is constant time (O(1)) whereas for a LinkedList
is it proportional to the size of the list (O(n)).
在上面的示例中,List
实现无法“保存其位置”以提高未来迭代的效率。对于 aArrayList
它并不重要,因为 的复杂性/成本get
是恒定时间(O(1)),而对于 aLinkedList
它与列表的大小(O(n))成正比。
For more information about the computational complexity of the built-in Collections
implementations, check out this question.
有关内置Collections
实现的计算复杂性的更多信息,请查看此问题。
Enhanced for loop(nicely explained in this question)
增强的for 循环(在这个问题中有很好的解释)
for (E element : list) {
// 1 - can call methods of element
// ...
}
Iterator
迭代器
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
ListIterator
列表迭代器
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
Functional Java
函数式 Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach, Stream.forEach, ...
Iterable.forEach, Stream.forEach, ...
(A map method from Java 8's Stream API (see @i_am_zero's answer).)
(来自 Java 8 的 Stream API 的映射方法(参见 @i_am_zero 的回答)。)
In Java 8 collection classes that implement Iterable
(for example, all List
s) now have a forEach
method, which can be used instead of the for loop statementdemonstrated above. (Here is another questionthat provides a good comparison.)
在 Java 8 中实现的集合类Iterable
(例如,所有List
s)现在有一个forEach
方法,可以用来代替上面演示的for 循环语句。(这是另一个提供了很好比较的问题。)
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
What other ways are there, if any?
如果有的话,还有哪些方法?
(BTW, my interest does not stem at all from a desire to optimize performance; I just want to know what forms are available to me as a developer.)
(顺便说一句,我的兴趣根本不是出于优化性能的愿望;我只是想知道作为开发人员的我可以使用哪些形式。)
采纳答案by Ted Hopp
The three forms of looping are nearly identical. The enhanced for
loop:
这三种循环形式几乎相同。增强for
循环:
for (E element : list) {
. . .
}
is, according to the Java Language Specification, identicalin effect to the explicit use of an iterator with a traditional for
loop. In the third case, you can only modify the list contents by removing the current element and, then, only if you do it through the remove
method of the iterator itself. With index-based iteration, you are free to modify the list in any way. However, adding or removing elements that come before the current index risks having your loop skipping elements or processing the same element multiple times; you need to adjust the loop index properly when you make such changes.
根据Java 语言规范,这与显式使用带有传统for
循环的迭代器的效果相同。在第三种情况下,您只能通过删除当前元素来修改列表内容,然后,只有通过remove
迭代器本身的方法才能进行。使用基于索引的迭代,您可以以任何方式自由修改列表。但是,添加或删除当前索引之前的元素可能会导致循环跳过元素或多次处理同一元素;进行此类更改时,您需要正确调整循环索引。
In all cases, element
is a reference to the actual list element. None of the iteration methods makes a copy of anything in the list. Changes to the internal state of element
will always be seen in the internal state of the corresponding element on the list.
在所有情况下,element
是对实际列表元素的引用。没有任何迭代方法会复制列表中的任何内容。内部状态的变化element
总是会在列表中相应元素的内部状态中看到。
Essentially, there are only two ways to iterate over a list: by using an index or by using an iterator. The enhanced for loop is just a syntactic shortcut introduced in Java 5 to avoid the tedium of explicitly defining an iterator. For both styles, you can come up with essentially trivial variations using for
, while
or do while
blocks, but they all boil down to the same thing (or, rather, two things).
本质上,只有两种方法可以迭代列表:使用索引或使用迭代器。增强的 for 循环只是 Java 5 中引入的一种语法快捷方式,以避免显式定义迭代器的乏味。对于这两种风格,您可以使用for
,while
或do while
块提出本质上微不足道的变化,但它们都归结为同一件事(或者,更确切地说,是两件事)。
EDIT: As @iX3 points out in a comment, you can use a ListIterator
to set the current element of a list as you are iterating. You would need to use List#listIterator()
instead of List#iterator()
to initialize the loop variable (which, obviously, would have to be declared a ListIterator
rather than an Iterator
).
编辑:正如@iX3 在评论中指出的那样,您可以ListIterator
在迭代时使用 a来设置列表的当前元素。您需要使用List#listIterator()
而不是List#iterator()
初始化循环变量(显然,必须声明为 aListIterator
而不是Iterator
)。
回答by shieldgenerator7
You could always switch out the first and third examples with a while loop and a little more code. This gives you the advantage of being able to use the do-while:
你总是可以用一个 while 循环和更多的代码来切换第一个和第三个例子。这为您提供了能够使用 do-while 的优势:
int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());
Of course, this kind of thing might cause a NullPointerException if the list.size() returns 0, becuase it always gets executed at least once. This can be fixed by testing if element is null before using its attributes / methods tho. Still, it's a lot simpler and easier to use the for loop
当然,如果 list.size() 返回 0,这种事情可能会导致 NullPointerException,因为它总是至少执行一次。这可以通过在使用元素的属性/方法之前测试元素是否为空来解决。尽管如此,使用 for 循环要简单得多
回答by Mario Rossi
I don't know what you consider pathological, but let me provide some alternatives you could have not seen before:
我不知道你认为什么是病态的,但让我提供一些你以前可能没有见过的替代方案:
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
Or its recursive version:
或者它的递归版本:
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
Also, a recursive version of the classical for(int i=0...
:
此外,经典的递归版本for(int i=0...
:
void visit(List<E> list,int pos) {
if( pos >= list.size() ) return;
E element= list.get(pos) ;
....
visit(list,pos+1);
}
I mention them because you are "somewhat new to Java" and this could be interesting.
我提到它们是因为您“对 Java 有点陌生”,这可能很有趣。
回答by iX3
Example of each kind listed in the question:
问题中列出的每种类型的示例:
ListIterationExample.java
ListIterationExample.java
import java.util.*;
public class ListIterationExample {
public static void main(String []args){
List<Integer> numbers = new ArrayList<Integer>();
// populates list with initial values
for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
numbers.add(i);
printList(numbers); // 0,1,2,3,4,5,6,7
// replaces each element with twice its value
for (int index=0; index < numbers.size(); index++) {
numbers.set(index, numbers.get(index)*2);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// does nothing because list is not being changed
for (Integer number : numbers) {
number++; // number = new Integer(number+1);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// same as above -- just different syntax
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
number++;
}
printList(numbers); // 0,2,4,6,8,10,12,14
// ListIterator<?> provides an "add" method to insert elements
// between the current element and the cursor
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.add(number+1); // insert a number right before this
}
printList(numbers); // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
// Iterator<?> provides a "remove" method to delete elements
// between the current element and the cursor
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
if (number % 2 == 0) // if number is even
iter.remove(); // remove it from the collection
}
printList(numbers); // 1,3,5,7,9,11,13,15
// ListIterator<?> provides a "set" method to replace elements
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.set(number/2); // divide each element by 2
}
printList(numbers); // 0,1,2,3,4,5,6,7
}
public static void printList(List<Integer> numbers) {
StringBuilder sb = new StringBuilder();
for (Integer number : numbers) {
sb.append(number);
sb.append(",");
}
sb.deleteCharAt(sb.length()-1); // remove trailing comma
System.out.println(sb.toString());
}
}
回答by eugene82
A JDK8-style iteration:
JDK8 风格的迭代:
public class IterationDemo {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().forEach(elem -> System.out.println("element " + elem));
}
}
回答by Yu Chen
Right, many alternatives are listed. The easiest and cleanest would be just using the enhanced for
statement as below. The Expression
is of some type that is iterable.
是的,列出了许多替代方案。最简单和最干净的方法是使用如下的增强for
语句。在Expression
一些类型,它是可迭代的。
for ( FormalParameter : Expression ) Statement
For example, to iterate through, List<String> ids, we can simply so,
例如,要遍历 List<String> ids,我们可以简单地这样,
for (String str : ids) {
// Do something
}
回答by amarseillan
The basic loop is not recommended as you do not know the implementation of the list.
不建议使用基本循环,因为您不知道列表的实现。
If that was a LinkedList, each call to
如果那是一个 LinkedList,则每次调用
list.get(i)
would be iterating over the list, resulting in N^2 time complexity.
将迭代列表,导致 N^2 时间复杂度。
回答by akhil_mittal
In Java 8we have multiple ways to iterate over collection classes.
在Java 8 中,我们有多种方法来迭代集合类。
Using Iterable forEach
使用可迭代的 forEach
The collections that implement Iterable
(for example all lists) now have forEach
method. We can use method-referenceintroduced in Java 8.
实现的集合Iterable
(例如所有列表)现在有forEach
方法。我们可以使用Java 8 中引入的方法引用。
Arrays.asList(1,2,3,4).forEach(System.out::println);
Using Streams forEach and forEachOrdered
使用流 forEach 和 forEachOrdered
We can also iterate over a list using Streamas:
我们还可以使用Stream迭代列表,如下所示:
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);
We should prefer forEachOrdered
over forEach
because the behaviour of forEach
is explicitly nondeterministic where as the forEachOrdered
performs an action for each element of this stream, in the encounter order of the stream if the stream has a defined encounter order. So forEach does not guarantee that the order would be kept.
我们应该优先考虑forEachOrdered
,forEach
因为 的行为forEach
是明确不确定的,其中forEachOrdered
为该流的每个元素执行操作,如果流具有定义的遇到顺序,则按照流的遇到顺序。所以 forEach 不保证订单会被保留。
The advantage with streams is that we can also make use of parallel streams wherever appropriate. If the objective is only to print the items irrespective of the order then we can use parallel stream as:
流的优点是我们还可以在适当的地方使用并行流。如果目标只是打印项目而不管顺序,那么我们可以使用并行流:
Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
回答by CoolMind
For a backward search you should use the following:
对于向后搜索,您应该使用以下内容:
for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
SomeClass item = iterator.previous();
...
item.remove(); // For instance.
}
If you want to know a position, use iterator.previousIndex(). It also helps to write an inner loop that compares two positions in the list (iterators are not equal).
如果你想知道一个位置,使用 iterator.previousIndex()。它还有助于编写一个内部循环来比较列表中的两个位置(迭代器不相等)。