我想在列表中最后一次出现特定项目后获取该项目。 例如
List<Bean> list = ArrayList<Bean>() {{
add(new Bean(1, null)); // null
add(new Bean(2, "text2"));
add(new Bean(3, "text3"));
add(new Bean(4, "text4"));
add(new Bean(5, null)); // null last occurence
add(new Bean(6, "text6"); // want this one.
}}
我想在最后一次出现带有空文本的Bean
之后获取该项目,例如上面的 Bean id 6。
Atomic
:
来处理它们
AtomicBoolean isPreviousNull = new AtomicBoolean(false);
AtomicReference<Bean> lastOccurrence = new AtomicReference<>(null);
list.forEach(item -> {
if (item.text == null) {
isPreviousNull.set(true);
} else if (isPreviousNull.get()) {
isPreviousNull.set(false);
lastOccurrence.set(item);
}
});
System.out.println(lastOccurrence.get());
Bean firstAfterNull = IntStream.iterate(list.size() - 2, i -> i - 1)
.limit(list.size() - 1) //or better .takeWhile(i -> i >= 0) on Java 9+
.filter(i -> null == list.get(i).getText())
.boxed()
.findFirst()
.map(i -> i + 1)
.map(list::get)
.orElse(null);
Bean lastFound = null;
Iterator<Bean> it = list.iterator();
if (it.hasNext()) {
Bean prev = it.next();
while (it.hasNext()) {
Bean curr = it.next();
if (prev.text == null) {
lastFound = curr;
}
prev = curr;
}
}
您还可以使用
ListIterator
反向迭代列表,并在找到第一个匹配项后立即停止。
Map<String, Bean> map = new HashMap<>();
for (Iterator<Bean> it = list.iterator(); it.hasNext(); )
if (it.next().getText() == null && it.hasNext())
map.put(null, it.next());
System.out.println(map.get(null));
直播版本:
lastFound = IntStream.range(0, list.size())
.collect(() -> new HashMap<String, Bean>(), (m, i) ->
{
if (list.get(i).getText() == null && (i + 1) < list.size())
m.put(null, list.get(i + 1));
}
, HashMap::putAll)
.get(null);
Stream
的解决方法之一是跟踪
text
中 Bean
的最后一个索引。Map<String, Integer> lastIndexMap = IntStream.range(0, list.size())
.boxed()
.collect(Collectors.toMap(a -> list.get(a).getText(), a -> a, Integer::max));
通过这种方式,您可以轻松访问特定文本的最后一个索引之后的下一个元素。
Bean afterLastIndexOfNull = list.get(lastIndexMap.get(null) + 1);
尤其是在您的情况下,如果我将null
文本值转换为
null
中的Map
键。应该高度劝阻他们,因此您可以选择实现一个包装器将 null
转换为某些默认文本,然后也基于相同的文本进行查找。
陷阱 list.get(lastIndexMap.get(<key>) + 1)
请注意,根据您进一步查询的内容,需要进行权衡。如果您想准备查找文本列表。这种方法可能对于单次迭代和更快的访问很有用。
但另一方面,如果这只是一次性计算,那么值得将信息最小化为单个索引。 Holger 也已经提出了解决方案。
OptionalInt lastIndex = IntStream.range(0, list.size())
.filter(ix -> list.get(ix).getText() == null)
.reduce((a, b) -> b);
Optional<Bean> opt = IntStream
.iterate(list.size() - 2, i -> i >= 0, i -> i - 1)
.filter(i -> list.get(i).getText() == null)
.mapToObj(i -> list.get(i + 1)).findFirst();
System.out.print(
opt.isPresent() ? opt.get().getText() : "None Found");
使用
partitioningBy
int index = IntStream.range(0, items.size())
.boxed()
.collect(partitioningBy(i -> items.get(i).getText() == null, reducing((l, r) -> r)))
.get(true).get() + 1;
Bean element = items.get(i);
这依赖于定位元素。如果此类元素不存在,则会抛出 NoSuchElementException
或 ArrayIndexOutOfBoundsException
。或者,您也可以直接执行
+ 1
而不是 .mapToObj(i -> i + 1)
,而不是
.boxed()
,这会产生相同的结果。
这是在即将推出的 Java 24 中使用
windowSliding
Stream Gatherers功能的一部分添加:
Optional<Bean> match = list.reversed().stream()
.gather(Gatherers.windowSliding(2))
.filter(l -> l.getLast().getText() == null)
.map(List::getFirst)
.findFirst();
这会在每对元素上流式传输列表的反向视图。 当遇到最后一个元素的文本为 null
的对时,将保留该对(请注意,这是向后的,因为列表是相反的)。 该对的第一个元素是原始列表中位于带有 null
文本的元素之后的元素,因此保留该元素。 最后,通过
findFirst
使用第一个元素,其余元素被丢弃(由于短路而没有被处理)。
详细分解
// [(1,null),(2,"t2"),(3,null),(4,"t4"),(5,"t5")]
List<Bean> list = List.of(
new Bean(1, null),
new Bean(2, "t2"),
new Bean(3, null),
new Bean(4, "t4"),
new Bean(5, "t5")
);
list // [(1,null),(2,"t2"),(3,null),(4,"t4"),(5,"t5")]
.reversed() // [(5,"t5"),(4,"t4"),(3,null),(2,"t2"),(1,null)]
.stream() // [(5,"t5"),(4,"t4"),(3,null),(2,"t2"),(1,null)]
.gather(Gatherers.windowSliding(2)) // [[(5,"t5"),(4,"t4")],[(4,"t4"),(3,null)],[(3,null),(2,"t2")],[(2,"t2"),(1,null)]]
.filter(l -> l.getLast().getText() == null) // [[(4,"t4"),(3,null)],[(2,"t2"),(1,null)]]
.map(List::getFirst) // [(4,"t4"),(2,"t2")]
.findFirst(); // (4,"t4")