如何在Java 8中获取findFirst()的索引?

16
我有以下代码:

I have the following code:

ArrayList <String> entries = new ArrayList <String>();

entries.add("0");
entries.add("1");
entries.add("2");
entries.add("3");

String firstNotHiddenItem = entries.stream()
                    .filter(e -> e.equals("2"))
                    .findFirst()
                    .get();

我需要知道第一个返回元素的索引,因为我需要在entries ArrayList中编辑它。据我所知,get()返回元素的值而不是引用。我该使用什么方法?

int indexOf(Object o)

改为什么?

4个回答

20

您可以使用 IntStream 来获取元素的索引,例如:

int index = IntStream.range(0, entries.size())
                     .filter(i -> "2".equals(entries.get(i)))
                     .findFirst().orElse(-1);

但是你应该使用 List::indexOf 方法,因为它更加简洁、表达更加精准,并且计算结果也一样。


9
List.indexOf和使用filterfindFirst的流之间的主要区别在于,indexOf使用列表元素类的equals方法,而流方法允许您基于与元素“相等性”无关的可能复杂的谓词进行搜索。 - fps

8

直接处理流元素时,无法确定它们在流中的位置,因此不能直接进行。

但是,如果您准备好放手...

int[] position = {-1};

String firstNotHiddenItem = entries.stream()
        .peek(x -> position[0]++)  // increment every element encounter
        .filter("2"::equals)
        .findFirst()
        .get();

System.out.println(position[0]); // 2

使用int[]而不是简单的int,是为了绕过“有效地最终”(effectively final)的要求;对数组的引用是常量,只有它的内容会改变。
还要注意使用方法引用"2"::equals而不是lambda表达式e -> e.equals("2"),这不仅避免了可能出现的NPE(如果流元素为空),更重要的是看起来很酷。
更易于理解的版本如下:
AtomicInteger position = new AtomicInteger(-1);

String firstNotHiddenItem = entries.stream()
        .peek(x -> position.incrementAndGet())  // increment every element encounter
        .filter("2"::equals)
        .findFirst()
        .get();

position.get(); // 2

1
完美的答案!简单而优雅,正好符合我的要求!谢谢! - Andrei
你还需要用 "-1" 在第二种变量中初始化变量positionAtomicInteger position = new AtomicInteger(-1); - einsA
亲爱的们,实际上我有一个类似的需求,但是有些不同。我有一个字符串val={1,2,3,4,5},我想要在原始数组中获得元素的索引,也就是说,我需要知道String[] val中"3"元素的索引,然后存储元素地址,例如val[2]是"3"。不使用if语句,只用Java8。 - mi_mo
@milad 这段代码会给你想要的东西。 - Bohemian
亲爱的@Bohemian,我尝试了这段代码,但它只返回一个计数器数字,请假设我有一个数组: int[] arr={1,2,3,4,5,6,..,15,...,20}, 如果每个元素都是arr[I] % 3 == 0,那么我应该将其插入到相应的String[] strArray中,例如: arr={1,2,3,4,5,6,..,15,...,20} 我必须要有strArray={"1","2","Test",...,"Test","16","17","18","19","20"},谢谢。 - mi_mo
@milad List<String> out = IntStream.of(intArray).mapToObj(n -> n % 3 == 0 ? "测试" : String.valueOf(n)).collect(Collectors.toList()); - Bohemian

0

是的,您应该使用indexOf("2")。正如你可能已经注意到的那样,任何基于流的解决方案都具有更高的复杂性,而没有提供任何好处。

在这种特定情况下,性能上没有明显的差异,但过度使用流可能会导致严重的性能退化,例如当使用map.entrySet().stream().filter(e -> e.getKey().equals(object)).map(e -> e.getValue())而不是一个简单的map.get(object)

集合操作可以利用它们已知的结构,而大多数流操作意味着线性搜索,因此真正的集合操作更可取。

当然,如果没有类似集合操作的情况,例如当您的谓词不是简单的相等测试时,流 API 可能是正确的工具。如“在 Java 8 中有一种简洁迭代带索引的流的方法吗?”所示,涉及索引的任何任务的解决方案都是通过使用索引作为起点,例如通过IntStream.range,并通过List.get(int)访问列表。如果源不是数组或随机访问List,则没有同样干净和高效的解决方案。有时,循环可能是最简单和最有效的解决方案。

0

使用Java 8和Eclipse Collections,这将能够正常工作。

int firstIndex = ListIterate.detectIndex(entries, "2"::equals);

如果您使用 MutableList,则可以简化代码如下:
MutableList<String> entries = Lists.mutable.with("0", "1", "2", "3");
int firstIndex = entries.detectIndex("2"::equals);

还有一种方法可以找到最后一个索引。

int lastIndex = entries.detectLastIndex("2"::equals);

注意:我是 Eclipse Collections 的提交者


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接