在Java中迭代列表的方法

作为Java语言的新手,我试图熟悉遍历列表(或其他集合)的所有方法(或至少是非病态的方法)以及每种方法的优缺点。

给定一个List<E> list对象,我知道以下方法来遍历所有元素:

基本的 循环(当然,也有等效的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指出的,这种形式是一个糟糕的选择 迭代Lists,因为的实际实现 get方法可能不如使用Iterator方法有效。 例如,LinkedList实现必须遍历所有的 在I之前的元素获取第I个元素 在上面的例子中,List实现没有办法 “保存其位置”以使未来的迭代更有效。 对于ArrayList,这并不重要,因为get的复杂度/代价是常数时间(O(1)),而对于LinkedList,它与列表的大小(O(n))成正比。< / p >

有关内置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

# eyz0, # eyz1,…

(一个来自Java 8的流API的映射方法(参见@i_am_zero的答案)。)

在Java 8实现Iterable(例如,所有# eyz1)的集合类中,现在有一个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).

如果有的话,还有其他办法吗?

(顺便说一下,我的兴趣根本不是出于对优化性能的渴望;我只是想知道作为开发人员,我有哪些表单可以使用。)

1175894 次浏览

您总是可以用一个while循环和更多的代码来切换第一个和第三个示例。这让你能够使用do-while的优势:

int i = 0;
do{
E element = list.get(i);
i++;
}
while (i < list.size());

当然,如果list.size()返回0,这类事情可能会导致NullPointerException,因为它总是至少执行一次。这可以通过在使用元素的属性/方法之前测试元素是否为空来修复。不过,使用for循环要简单得多

我不知道你认为什么是病态的,但让我提供一些你以前可能没有见过的替代方案:

List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}

或者它的递归版本:

void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}

此外,经典的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);
}

我提到它们是因为您“对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());
}
}

循环的三种形式几乎相同。增强的for循环:

for (E element : list) {
. . .
}

是,根据Java语言规范相同的有效地显式使用迭代器与传统的for循环。在第三种情况下,您只能通过删除当前元素来修改列表内容,并且只能通过迭代器本身的remove方法来修改。使用基于索引的迭代,您可以自由地以任何方式修改列表。但是,添加或删除位于当前索引之前的元素可能会导致循环跳过元素或多次处理同一元素;在进行此类更改时,需要适当地调整循环索引。

在所有情况下,element都是对实际列表元素的引用。所有迭代方法都不会复制列表中的任何内容。对element内部状态的更改将始终在列表中相应元素的内部状态中看到。

本质上,只有两种方法可以遍历列表:使用索引或使用迭代器。增强的for循环只是Java 5中引入的语法快捷方式,以避免显式定义迭代器的单调乏味。对于这两种风格,你可以使用forwhiledo while块来想出本质上微不足道的变化,但它们都归结为同一件事(或者更确切地说,两件事)。

编辑:正如@iX3在评论中指出的那样,可以在迭代时使用ListIterator来设置列表的当前元素。您需要使用List#listIterator()而不是List#iterator()来初始化循环变量(显然,必须声明为ListIterator而不是Iterator)。

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

是的,列出了很多替代方案。最简单、最干净的方法是使用增强的for语句,如下所示。Expression是某种可迭代的类型。

for ( FormalParameter : Expression ) Statement

例如,要遍历List<String> id,我们可以简单地这样,

for (String str : ids) {
// Do something
}

不建议使用基本循环,因为您不知道列表的实现。

如果那是一个LinkedList,每次调用

list.get(i)

将在列表上迭代,导致N^2的时间复杂度。

Java 8中,我们有多种方法迭代集合类。

使用Iterable forEach

实现Iterable的集合(例如所有列表)现在有forEach方法。我们可以使用Java 8中引入的方法引用

Arrays.asList(1,2,3,4).forEach(System.out::println);

使用forEach和forEachOrdered流

我们还可以使用遍历列表,如下:

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);

我们应该更喜欢forEachOrdered而不是forEach,因为forEach的行为是显式的不确定的,因为forEachOrdered为这个流的每个元素执行一个操作,如果流有一个定义的遇到顺序,那么在流的遇到顺序中。因此forEach并不保证顺序会被保持。

流的优点是我们还可以在适当的地方使用并行流。如果目标只是打印项目而不考虑顺序,那么我们可以使用并行流如下:

Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);

对于向后搜索,您应该使用以下命令:

for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
SomeClass item = iterator.previous();
...
item.remove(); // For instance.
}

如果你想知道一个位置,使用iterator.previousIndex()。它还有助于编写一个内部循环,比较列表中的两个位置(迭代器不相等)。

你可以从Java 8开始使用forEach:

 List<String> nameList   = new ArrayList<>(
Arrays.asList("USA", "USSR", "UK"));


nameList.forEach((v) -> System.out.println(v));
java 8中,您可以使用List.forEach()方法和lambda expression来迭代列表。
< / p >
import java.util.ArrayList;
import java.util.List;


public class TestA {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Apple");
list.add("Orange");
list.add("Banana");
list.forEach(
(name) -> {
System.out.println(name);
}
);
}
}

上面你会发现所有迭代LIST的不同方法。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;


public class test1 {


public static void main(String[] args) {
//******* Exercise 1 : Write a Java program to create a new array list, add some colors (string) and print out the collection.
List<String> colors = new ArrayList<String>();
colors.add("Black");
colors.add("Red");
colors.add("Green");
colors.add("Blue");
System.out.println(colors);
    

    

//******* Exercise 2 : Write a Java program to iterate through all elements in a array list.
System.out.println("//******* Exercise 2");
List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    

// iteration type 1 : using FOR loop
System.out.println("// iteration type 1");
for(Integer nb : list2) {
System.out.print(nb + ", ");
}
System.out.println("\n");
    

// iteration type 2 : using FOR loop
System.out.println("// iteration type 2");
for(int i=0; i < list2.size(); i++) {
System.out.print(list2.get(i) + ", ");
}System.out.println("\n");
    

// iteration type 3  : using Do-While loop
System.out.println("// iteration type 3");
int index21 = 0;
    

do {
System.out.print(list2.get(index21) + ", ");
index21++;
}while(index21<list2.size());
System.out.println("\n");
    

    

// iteration type 4  : using While loop
System.out.println("// iteration type 4");
int index22 = 0;
while(index22<list2.size()) {
System.out.print(list2.get(index22) + ", ");
index22++;
}


System.out.println("\n");
    

    

// iteration type 5  : using  Iterable forEach loop
System.out.println("// iteration type 5");
list2.forEach(elt -> {
System.out.print(elt + ", ");
});


System.out.println("\n");
    

    

// iteration type 6  : using  Iterator
System.out.println("// iteration type 6");
Iterator<Integer> listIterator = list2.iterator();
while(listIterator.hasNext()) {
System.out.print( listIterator.next() + ", ");
}
    

System.out.println("\n");
    

// iteration type 7  : using  Iterator (From the beginning)
System.out.println("// iteration type 7");
ListIterator<Integer> listIterator21 = list2.listIterator(list2.size());
while(listIterator21.hasPrevious()) {
System.out.print( listIterator21.previous() + ", ");
}


System.out.println("\n");
    

// iteration type 8  : using  Iterator (From the End)
System.out.println("// iteration type 8");
ListIterator<Integer> listIterator22 = list2.listIterator();
while(listIterator22.hasNext()) {
System.out.print( listIterator22.next() + ", ");
}


System.out.println("\n");
}


}

在Java 8或以上版本中,可以使用forEach()方法迭代Hashset。

import java.util.HashSet;


public class HashSetTest {
public static void main(String[] args) {
        

HashSet<String> hSet = new HashSet<String>();
          

// Adding elements into your HashSet usind add()
hSet.add("test1");
hSet.add("test2");
hSet.add("test3");
      

// Iterating over hash set items
hSet.forEach(x -> System.out.println(x));
// Or you can write shorter:
hSet.forEach(System.out::println);
}
}