是否有一个Java 8版本的内置Python enumerate?
3年前,这里也提出了一个类似的问题: 我非常感谢是否有一个Java 8版本的内置Python enumerate?,java,python,java-8,Java,Python,Java 8,3年前,这里也提出了一个类似的问题: 我非常感谢listIterator()解决方案。尽管如此,我现在仍在大量使用新的streams和Lambda(在JDK 8中引入)并想知道:有没有一种优雅的方法来获取当前正在处理的元素的索引?下面是我的,但我并不觉得它特别吸引人 IntStream.range(0, myList.size()) .mapToObj(i -> doSthWith(myList.get(i), i)); 这可能有一些解决办法 您可以使用一个Atomi
listIterator()
解决方案。尽管如此,我现在仍在大量使用新的streams和Lambda(在JDK 8中引入)并想知道:有没有一种优雅的方法来获取当前正在处理的元素的索引?下面是我的,但我并不觉得它特别吸引人
IntStream.range(0, myList.size())
.mapToObj(i -> doSthWith(myList.get(i), i));
这可能有一些解决办法 您可以使用一个
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()));
final迭代器a=IntStream.range(0,list.size()).Iterator();
list.stream().forEach(i->System.out.println(i+“-”+a.next());
嗯,我不确定是否还有其他更好的替代方案(当然),但这就是我目前的想法
注意,它假设您没有使用并行流。在后一种情况下,不可能像这样获得元素与其列表中原始索引的映射。您提供的链接中的python示例是:
final Iterator<Integer> a = IntStream.range(0, list.size()).iterator();
list.stream().forEach(i -> System.out.println(i+"-"+a.next()));
>>> 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
这个问题以前有人问过几个问题。关键的观察结果是,除非您有完美的大小和分割信息(基本上,如果您的源是一个数组),否则这将是一个仅顺序的操作 你提出的“不吸引人”的答案是:
IntStream.range(0, myList.size())
.mapToObj(i -> doSthWith(myList.get(i), i));
当
myList
是一个ArrayList
或其他具有快速O(1)索引get操作的列表时,它实际上非常有效,并且可以干净地并行化。所以我认为这没什么问题 如果您想要更清晰的语法,只需结束一些通话:
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)));
}
}
如果您想要一个高效的LinkedList解决方案,那么您需要在不调用LinkedList.get()的情况下处理该问题。如果您想要一个同样适用于非
随机访问
列表的解决方案,您可以编写一个简单的实用程序方法:
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,(s,i)->System.out.println(i+“\t”+s))一样使用
..尽管它不太常用,enumerate
可用于任何iterable(您只需找到索引的用途,因为它不再对应于序列)。我怀疑Java解决方案也可以毫无问题地完成同样的工作,尽管它将非常复杂,足以在自己的类中提取并重新使用(而不是像您的示例中那样在一行中)。这还取决于您使用的列表的具体实现,但在LinkedList上调用get是O(n)。所以不确定这个解决方案是否值得。这就是为什么我如此希望一个“哦,你就是这样做的”的解决方案来自一个有更高知识的人。我显然不想向任何人推荐上述方法@RadosławŁazarz一个解决方法是执行final AtomicInteger atom=new AtomicInteger();list.stream().forEach(i->System.out.println(i+“-”+atom.getAndIncrement())代码>任何使用第三方库(如apache collections/google Guava)的更干净的解决方案?事实上,如果使用并行流,您(无声地)只会得到错误的答案。坏主意!无需使用list.stream().forEach(…)
。不仅list.forEach(…)
更简单,它还保证尊重元素顺序,而list.stream().forEach(…)
则不是(与list.stream().forEachOrdered(…)
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());
}
}