Java 8 Stream 在列表中查找元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35928747/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Stream to find element in list
提问by Gengis Khan
I have the following class:
我有以下课程:
public class Item {
int id;
String name;
// few other fields, contructor, getters and setters
}
I have a list of Items. I want to iterate through the list and find the instance which has a particular id. I'm trying to do it through streams.
我有一个项目列表。我想遍历列表并找到具有特定 ID 的实例。我正在尝试通过流来做到这一点。
public void foobar() {
List<Item> items = getItemList();
List<Integer> ids = getIdsToLookup();
int id, i = ids.size() - 1;
while (i >= 0) {
id = ids.get(i);
Optional<Item> item = items
.stream()
.filter(a -> a.getId() == id)
.findFirst();
// do stuff
i--;
}
}
Is this the best way to iterate over the list and get the element I need? Also, I get an error on the filter line for id which says variables used in lambda expressions must be final or effectively final. Maybe I can define id inside the while loop, that should get rid of the exception. Thanks.
这是迭代列表并获取我需要的元素的最佳方法吗?此外,我在 id 的过滤器行上收到一个错误,表示 lambda 表达式中使用的变量必须是最终的或有效的最终。也许我可以在 while 循环中定义 id ,这应该可以消除异常。谢谢。
采纳答案by Holger
If you have lots of ids to search, it's recommended to use a solution which does it in a single pass rather than doing a linear search for each id:
如果您有很多 id 需要搜索,建议使用一次性完成的解决方案,而不是对每个 id 进行线性搜索:
Map<Integer,Optional<Item>> map=ids.stream()
.collect(Collectors.toMap(id -> id, id -> Optional.empty()));
items.forEach(item ->
map.computeIfPresent(item.getId(), (i,o)->o.isPresent()? o: Optional.of(item)));
for(ListIterator<Integer> it=ids.listIterator(ids.size()); it.hasPrevious();) {
map.get(it.previous()).ifPresent(item -> {
// do stuff
});
}
The first statement simply create a map out of the ids list, mapping each search id to an empty Optional
.
第一个语句简单地从 ids 列表中创建一个映射,将每个搜索 id 映射到一个空的Optional
。
The second statement iterates over the items using forEach
and for each item, it checks whether there's a mapping from its id to an empty Optional
and will replace it with an Optional
encapsulating the item, if there is such a mapping, all in one operation, computeIfPresent
.
第二个语句使用forEach
和 对于每个项目迭代项目,它检查是否存在从其 id 到空的映射Optional
,并将用Optional
封装项目替换它,如果有这样的映射,全部在一个操作中computeIfPresent
。
The last for
loop iterates backwards over the ids
list, as you wished to process them in that order and perform the action if there's a non-empty Optional
. Since the map was initialized with all ids found in the list, get
will never return null
, it will return an empty Optional
, if the id was not found in the items
list.
最后一个for
循环向后遍历ids
列表,因为您希望按该顺序处理它们并在存在非空时执行操作Optional
。由于地图是使用列表中找到的所有 id 初始化的,get
因此永远不会返回,如果在列表中找不到该 id null
,它将返回一个空Optional
的items
。
That way, assuming that the Map
's lookup has O(1)
time complexity, which is the case in typical implementations, the net time complexity changed from O(m×n)
to O(m+n)
…
这样一来,假设Map
的查找具有O(1)
时间复杂度,这是典型的实现的情况下,净时间复杂度从变化O(m×n)
到O(m+n)
...
回答by ByeBye
You can try use something like this:
你可以尝试使用这样的东西:
ids.forEach(id ->
list.stream()
.filter(p -> p.getId() == id)
.findFirst()
.ifPresent(p -> {
// do stuff here
});
);
Optional here shows that your filter method can return a empty stream, so if you call findFirst it can find one or zero elements.
此处可选显示您的过滤器方法可以返回一个空流,因此如果您调用 findFirst 它可以找到一个或零个元素。
回答by Federico Peralta Schaffner
If you want to stick with streams and iterate backwards, you could do it this way:
如果你想坚持使用流并向后迭代,你可以这样做:
IntStream.iterate(ids.size() - 1, i -> i - 1)
.limit(ids.size())
.map(ids::get) // or .map(i -> ids.get(i))
.forEach(id -> items.stream()
.filter(item -> item.getId() == id)
.findFirst().ifPresent(item -> {
// do stuff
}));
This code does the same as yours.
此代码与您的代码相同。
It iterates backwards, starting with a seed: ids.size() - 1
. The initial stream of int
s is limited in its size with limit()
, so that there are no negative int
s and the stream has the same size as the list of ids
. Then, a map()
operation converts the index to the actual id
that is at the ith position at the ids
list (this is done by means of invoking ids.get(i)
). Finally, the item is searched in the items
list the same way as in your code.
它向后迭代,从种子开始:ids.size() - 1
。int
s的初始流的大小受 限制limit()
,因此没有负数int
s 并且流具有与 的列表相同的大小ids
。然后,一个map()
操作将索引转换为列表中id
第 i 个位置的实际索引ids
(这是通过调用 完成的ids.get(i)
)。最后,在items
列表中搜索该项目的方式与您的代码相同。
回答by user_3380739
You want to find at most one item for each given id and do something with the found item, right? A bit more performance improvement:
您想为每个给定的 id 最多找到一个项目,并对找到的项目做一些事情,对吗?更多的性能改进:
Set<Integer> idsToLookup = new HashSet<>(getIdsToLookup()); // replace list with Set
items.stream()
.filter(e -> idsToLookup.remove(e.getId()))
.forEach(
/* doing something */
);