在Java中迭代列表的方法
对于Java语言而言,我尝试熟悉所有可能通过列表(或者其他集合)迭代的方式(或者至少是非病态方法)以及每种方式的优缺点。
给定一个List<E> list
对象,我知道以下方法循环遍历所有元素:
基本的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
// ...
}
注意:正如@amarseillan指出的那样,这种形式对迭代List
s来说是一个糟糕的选择,因为get
方法的实际实现可能不如使用Iterator
时那样高效。 例如, LinkedList
实现必须遍历i之前的所有元素才能获得第i个元素。
在上面的例子中, List
实现没有办法“保存它的位置”,以便将来的迭代更有效率。 对于ArrayList
它并不重要,因为get
的复杂性/成本是恒定时间(O(1)),而对于LinkedList
,它与列表的大小成正比(O(n))。
有关内置Collections
实现的计算复杂性的更多信息,请查看此问题。
增强for循环(在这个问题中很好地解释)
for (E element : list) {
// 1 - can call methods of element
// ...
}
迭代器
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
// ...
}
功能性Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach,Stream.forEach,...
(来自Java 8的Stream API的映射方法(请参阅@ i_am_zero的答案))
在实现Iterable
Java 8集合类(例如,所有List
)中现在有一个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).
还有什么其他方式,如果有的话?
(顺便说一下,我的兴趣根本不在于优化性能的愿望;我只是想知道作为开发人员可以使用哪些形式。)
循环的三种形式几乎完全相同。 增强的for
循环:
for (E element : list) {
. . .
}
根据Java语言规范,与传统for
循环显式使用迭代器相同。 在第三种情况下,只能通过删除当前元素来修改列表内容,然后只能通过迭代器本身的remove
方法来完成。 使用基于索引的迭代,您可以自由地以任何方式修改列表。 但是,添加或删除当前索引之前的元素可能会导致循环跳过元素或处理相同的元素多次; 您需要在进行此类更改时正确调整循环索引。
在所有情况下, element
都是对实际列表元素的引用。 这些迭代方法都不会在列表中生成任何副本。 element
内部状态的变化将始终在列表中相应元素的内部状态中看到。
本质上,只有两种方法可以遍历列表:使用索引或使用迭代器。 增强型for循环只是Java 5中引入的一种语法快捷方式,可以避免显式定义迭代器的麻烦。 对于这两种款式,你能想出使用基本上微不足道的变化for
, while
还是do while
块,但他们都归结为同样的事情(或者说,两件事情)。
编辑:作为@ IX3在评论中指出,你可以使用ListIterator
设置列表的当前元素为你迭代。 您需要使用List#listIterator()
而不是List#iterator()
来初始化循环变量(显然,必须将其声明为ListIterator
而不是Iterator
)。
问题中列出的每种类型的示例:
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());
}
}
不建议使用基本循环,因为您不知道列表的实现。
如果这是一个LinkedList,则每次调用
list.get(i)
将迭代列表,导致N ^ 2时间复杂度。
链接地址: http://www.djcxy.com/p/2969.html