我有以下代码:
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();
我需要知道第一个返回元素的索引是什么,因为我需要在条目内编辑它
ArrayList
。据我所知 get()
返回元素的值,而不是引用。我应该只使用
int indexOf(Object o)
相反?
您可以使用
IntStream
获取元素的索引,例如:
int index = IntStream.range(0, entries.size())
.filter(i -> "2".equals(entries.get(i)))
.findFirst().orElse(-1);
但是你应该使用
List::indexOf
方法,这是首选方法,因为它更简洁,更具表现力,并且计算出相同的结果。
您不能以简单的方式 - 流在没有上下文的情况下处理元素在流中的位置。
但是,如果您准备脱掉手套......
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
,是为了规避“有效最终”的要求;数组的引用是常量,只有其内容发生变化。
还要注意使用方法引用
"2"::equals
而不是lambdae -> e.equals("2")
,这不仅避免了可能的NPE(如果流元素是null
),更重要的是看起来way更酷。
更美味(更少恶意)的版本:
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
这将适用于 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 的提交者
是的,您应该使用
indexOf("2")
来代替。您可能已经注意到,任何基于流的解决方案都具有更高的复杂性,但没有提供任何好处。
在这种特定情况下,性能没有显着差异,但过度使用流可能会导致性能急剧下降,例如当使用
map.entrySet().stream().filter(e -> e.getKey().equals(object)).map(e -> e.getValue())
而不是简单的 map.get(object)
时。
集合操作可以利用其已知的结构,而大多数流操作意味着线性搜索。所以正版收藏运营更可取。
当然,如果没有集合操作,例如当您的谓词不是简单的相等测试时,Stream API 可能是正确的工具。如“Java 8 中是否有一种简洁的方法来迭代带有索引的流?”中所示,任何涉及索引的任务的解决方案都可以使用索引作为起点,例如通过
IntStream.range
,并通过 List.get(int)
访问列表。如果源不是数组或随机访问List
,则没有同样干净且有效的解决方案。有时,循环可能是最简单、最有效的解决方案。