Ways to iterate over a list in Java
对于Java语言有点陌生,我试图让自己熟悉所有的方法(或者至少是非病理性的),可以通过列表(或者其他集合)和每个方法的优缺点来迭代。
给定一个
1 2 3 4 5 6 7 8 | // 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所指出的,这种形式是一种糟糕的选择。因为实际实现了
在上面的示例中,
有关内置
1 2 3 4 5 | for (E element : list) { // 1 - can call methods of element // ... } |
迭代器
1 2 3 4 5 6 7 | 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 // ... } |
记录器
1 2 3 4 5 6 7 8 9 10 | 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
1 | list.stream().map(e -> e + 1); // Can apply a transformation function for e |
iterable.foreach,stream.foreach,…
(来自Java 8的流API的映射方法(参见@ IyAM0Z0的答案))
在Java 8中,实现EDCOX1、12、(例如,所有EDCOX1,3,s)的集合类现在有一个EDCOX1×14的方法,它可以用来代替上面演示的for循环语句。(这是另一个提供了很好比较的问题。)
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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). |
如果有的话,还有什么别的办法?
(顺便说一句,我的兴趣根本不是源于对优化性能的渴望;我只是想知道作为开发人员我可以使用哪些表单。)
三种循环形式几乎相同。增强型
1 2 3 | for (E element : list) { . . . } |
根据Java语言规范,它与使用传统EDCOX1×1循环的迭代器的显式使用效果相同。在第三种情况下,只能通过删除当前元素来修改列表内容,然后只有通过迭代器本身的
在所有情况下,
本质上,只有两种方法可以迭代列表:使用索引或使用迭代器。增强的for循环只是Java 5中引入的一种语法捷径,以避免显式定义迭代器的单调乏味。对于这两种样式,您可以使用
编辑:正如@ix3在注释中指出的那样,您可以使用
问题中列出的每种示例:
ListIterationExample.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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,则每次调用
1 | list.get(i) |
将遍历列表,导致n^2时间复杂性。
JDK8样式迭代:
1 2 3 4 5 6 7 |
在Java 8中,我们有多种方式对集合类进行迭代。
使用Iterable ForEach实现
使用流foreach和foreachored
我们还可以使用流迭代列表,如下所示:
1 2 |
我们应该更喜欢
流的优势在于,我们还可以在适当的情况下使用并行流。如果目标只是打印项目,不管顺序如何,那么我们可以使用并行流作为:
我不知道你认为什么是病理性的,但让我提供一些你以前没见过的替代品:
1 2 3 4 5 6 | List<E> sl= list ; while( ! sl.empty() ) { E element= sl.get(0) ; ..... sl= sl.subList(1,sl.size()); } |
或其递归版本:
1 2 3 4 5 6 | void visit(List<E> list) { if( list.isEmpty() ) return; E element= list.get(0) ; .... visit(list.subList(1,list.size())); } |
另外,经典
1 2 3 4 5 6 | void visit(List<E> list,int pos) { if( pos >= list.size() ) return; E element= list.get(pos) ; .... visit(list,pos+1); } |
我提到它们是因为你对Java有点"新",这可能很有趣。
可以从Java 8开始使用Frace:
1 2 3 4 |
在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
对于向后搜索,应使用以下内容:
1 2 3 4 5 | for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) { SomeClass item = iterator.previous(); ... item.remove(); // For instance. } |
如果要知道位置,请使用Iterator.PreviousIndex()。它还有助于编写一个内部循环来比较列表中的两个位置(迭代器不相等)。
对,列出了许多备选方案。最简单、最干净的方法就是使用下面的增强型
1 |
例如,要遍历list
1 2 3 |
您可以使用while循环和更多的代码来切换第一个和第三个示例。这使您能够在以下情况下使用Do:
1 2 3 4 5 6 | int i = 0; do{ E element = list.get(i); i++; } while (i < list.size()); |
当然,如果list.size()返回0,这种情况可能会导致nullPointerException,因为它总是至少执行一次。这可以通过在使用元素的属性/方法tho之前测试元素是否为空来解决。不过,使用for循环要简单得多。