当前位置: 首页 > 知识库问答 >
问题:

在Java中迭代列表的方法

方和顺
2023-03-14

由于对Java语言有些陌生,我正在尝试熟悉所有可以通过列表(或者其他集合)进行迭代的方法(或者至少是非病态的方法),以及每种方法的优缺点。

给定一个list list 对象,我知道以下循环所有元素的方法:

// 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方法的实际实现可能不如使用迭代器时那样高效。例如,linkedlist实现必须遍历i之前的所有元素以获得第i个元素。

在上面的示例中,list实现没有办法“保留它的位置”来提高未来迭代的效率。对于ArrayList来说,这并不重要,因为Get的复杂度/开销是恒定时间(O(1)),而对于LinkedList来说,它与列表的大小成正比(O(n))。

有关内置collections实现的计算复杂性的更多信息,请查看此问题。

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

    // ...
}
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

    // ...
}
list.stream().map(e -> e + 1); // Can apply a transformation function for e

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

在Java 8集合中,实现iterable(例如,所有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).

如果有的话,还有什么其他方法?

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

共有2个答案

徐博雅
2023-03-14

问题中列出的每种示例:

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());
     }
}
裴欣然
2023-03-14

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

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

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

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

本质上,只有两种方法可以对列表进行迭代:使用索引或使用迭代器。增强的for循环只是Java 5中引入的一个语法快捷方式,以避免显式定义迭代器的单调乏味。对于这两种样式,您都可以使用Forwhiledo while块来产生一些基本不重要的变化,但它们都归结为同一件事(或者更确切地说,是两件事)。

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

 类似资料:
  • 问题内容: 对于Java语言有些陌生,我试图使自己熟悉所有可能遍历列表(或其他集合)的方式(或至少是非病理性方式)以及每种方式的优缺点。 给定一个对象,我知道以下遍历所有元素的方式: 基本的for 循环(当然,也有等效的while/ do while循环) 注意:正如@a​​marseillan指出的那样,这种形式对于在s上进行迭代是一个糟糕的选择,因为该方法的实际实现可能不如使用时有效。例如,实

  • 我想知道,如何在Java8中使用流API迭代多级列表 根据Java8,我应该做如下的操作 我想要流利地做这件事(使用内部迭代)。任何解释都会很感激。

  • 问题内容: 如果我在java中的链表上使用了for-each循环,是否可以保证以它们在列表中出现的顺序迭代元素? 问题答案: 保证链表按顺序运行。 从文档中 有序集合(也称为序列)。该界面的用户可以精确控制列表中每个元素的插入位置。用户可以通过其整数索引(列表中的位置)访问元素,并在列表中搜索元素。 iterator() 以适当的顺序返回此列表中元素的迭代器。

  • 假设我有3个要关闭的Scanner实例。 我可以做 对于每个扫描仪。 或者我可以做类似的事情 使用Java8有没有更简单的方法? 类似的东西?

  • 主要内容:如何为每一行添加序号?本文章将介绍Thymeleaf标准表达式语法中的概念。我们将使用标记在模板中迭代产品列表。 编辑源代码以便将产品列表显示为表格行。已经将类的对象列表设置为具有变量名称的上下文模型(参考:中的实现)。 如果要上机实践,请参考:Thymeleaf+SpringMVC5示例项目。这里不再重复创建项目的过程,这里将只介绍如何使用标准表达式和标签。 这里创建一个Maven Web项目: thymeleaf-

  • 我正在尝试迭代一个列表,并尝试将该值打印为: 但是,只打印列表的大小,而不是在JSTL中定义的。有人帮忙吗?我想打印对象的所有属性。