是否有Java 8等效的Python枚举内置?

时间:2014-05-22 22:07:30

标签: java python java-8

3年前,这里也提出了类似的问题: Is there a Java equivalent of Python's 'enumerate' function?

我非常感谢listIterator()解决方案。不过,我现在对新流和lambdas(在JDK 8中引入)做了很多工作并且想知道:有没有一种优雅的方法来获取当前处理的元素的索引?我的下面列出了,但我觉得它并不特别吸引人。

IntStream.range(0, myList.size())
         .mapToObj(i -> doSthWith(myList.get(i), i));

5 个答案:

答案 0 :(得分:15)

此问题之前有过几个问题。关键的观察是,除非你有完美的大小和拆分信息(基本上,如果你的源是一个数组),那么这将是一个仅序贯操作。

"没有吸引力"回答你的建议:

IntStream.range(0, myList.size())
         .mapToObj(i -> doSthWith(myList.get(i), i));
myListArrayList或其他具有快速O(1)索引获取操作的列表并且干净地并行化时,

实际上非常有效。所以我认为它没有任何问题。

答案 1 :(得分:6)

可以有一些解决方法。

您可以使用AtomicInteger每次浏览流的元素时都会增加

final AtomicInteger atom = new AtomicInteger(); 
list.stream().forEach(i -> System.out.println(i+"-"+atom.getAndIncrement()));

或者使用来自另一个流的迭代器来获取索引(有点像你最初的想法)但更有效率,因为你没有在列表上调用get

final Iterator<Integer> a = IntStream.range(0, list.size()).iterator();
list.stream().forEach(i -> System.out.println(i+"-"+a.next()));

嗯,我不确定是否存在其他更好的选择(当然),但这就是我现在的想法。

请注意,假设您没有使用并行流。在后一种情况下,不可能这样做以获得元素与列表中原始索引的映射。

答案 2 :(得分:4)

如果您想要一个适用于非RandomAccess列表的解决方案,您可以自己编写一个简单的实用方法:

public static <T> void forEach(List<T> list, BiConsumer<Integer,? super T> c) {
    Objects.requireNonNull(c);
    if(list.isEmpty()) return;
    if(list instanceof RandomAccess)
        for(int i=0, num=list.size(); i<num; i++)
            c.accept(i, list.get(i));
    else
        for(ListIterator<T> it=list.listIterator(); it.hasNext(); ) {
            c.accept(it.nextIndex(), it.next());
    }
}

然后您可以像forEach(list, (i,s)->System.out.println(i+"\t"+s));

一样使用它

如果你交换元素和索引的顺序,你可以使用ObjIntConsumer而不是BiConsumer<Integer,? super T>来避免潜在的拳击开销:

public static <T> void forEach(List<T> list, ObjIntConsumer<? super T> c) {
    Objects.requireNonNull(c);
    if(list.isEmpty()) return;
    if(list instanceof RandomAccess)
        for(int i=0, num=list.size(); i<num; i++)
            c.accept(list.get(i), i);
    else
        for(ListIterator<T> it=list.listIterator(); it.hasNext(); ) {
            c.accept(it.next(), it.previousIndex());
    }
}

然后,像forEach(list, (s,i) -> System.out.println(i+"\t"+s)); ...

一样使用

答案 3 :(得分:2)

您提供的链接中的python示例是:

>>> numbers = ["zero", "one", "two"]
>>> for i, s in enumerate(numbers):
...     print i, s
... 
0 zero
1 one
2 two

有几种方法可以使用Java 8实现相同的输出,例如:

String[] numbers = {"zero", "one", "two"};
IntStream.range(0, numbers.length)
        .mapToObj(i -> i + " " + numbers[i])
        .forEach(System.out::println);

输出:

0 zero
1 one
2 two

答案 4 :(得分:2)

如果你想要更清晰的语法,那么只需结束一些调用:

public class IndexedValue<T> {
    public final int index;
    public final T value;
    public IndexedValue(int index, T value) {
        this.index = index;
        this.value = value;
    }
}

public class Itertools {
    public static <T> Stream<IndexedValue<T>> enumerate(ArrayList<T> lst) {
        return IntStream.range(0, lst.size())
            .mapToObj(i -> new IndexedValue<T>(i, lst.get(i)));
    }
}

然后使用它:

import static com.example.Itertools.enumerate;

enumerate(myList).map(i -> doSthWith(i.value, i.index));

如果你想要一个高效的LinkedList解决方案,那么你需要在不调用LinkedList.get()的情况下处理它。