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

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

基本的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指出的,这种形式是一个糟糕的选择 用于迭代列表,因为的实际实现 get方法可能没有使用Iterator时那么有效。 例如,LinkedList实现必须遍历所有的 在I之前的元素得到第I个元素。

在上面的例子中,List实现没有办法 “保存其位置”以使未来的迭代更有效。 对于数组列表来说,这并不重要,因为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

    // ...
}

Functional Java

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

可迭代的forEach,流。forEach,……

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

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

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

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


当前回答

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

其他回答

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

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

 nameList.forEach((v) -> System.out.println(v));

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

使用Iterable forEach

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

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

使用forEach和forEachOrdered流

我们还可以使用Stream as迭代列表:

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

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

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

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

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

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

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

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

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有些陌生”,这可能会很有趣。