繁体   English   中英

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

[英]Is there a Java 8 equivalent of Python enumerate built-in?

3年前,这里也提出了一个类似的问题: 是否存在类似于Python的'枚举'函数的Java?

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

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

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

你建议的“没有吸引力”的答案:

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

myList是一个ArrayList或其他具有快速O(1)索引获取操作的列表时,实际上是非常有效的,并且干净地并行化。 所以我觉得它没有错。

可以有一些解决方法。

您可以使用每次遍历流的元素时将递增的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()));

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

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

如果您想要一个适用于非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> 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));一样forEach(list, (s,i) -> System.out.println(i+"\\t"+s)); ...

您提供的链接中的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

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

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()的情况下处理它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM