[英]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.