我有以下课程:
public class Item {
int id;
String name;
// few other fields, contructor, getters and setters
}
我有一个项目列表。我想迭代列表并找到具有特定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--;
}
}
这是迭代列表并获得我需要的元素的最佳方法吗?另外,我在id的过滤行上得到一个错误,它表示lambda表达式中使用的变量必须是final或者有效的final。也许我可以在while循环中定义id,这应该摆脱异常。感谢。
答案 0 :(得分:9)
您可以尝试使用以下内容:
ids.forEach(id ->
list.stream()
.filter(p -> p.getId() == id)
.findFirst()
.ifPresent(p -> {
// do stuff here
});
);
此处可选显示您的过滤器方法可以返回空流,因此如果您调用findFirst,它可以找到一个或零个元素。
答案 1 :(得分:8)
如果您有很多要搜索的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
});
}
第一个语句只是从ids列表中创建一个地图,将每个搜索ID映射为空Optional
。
第二个语句使用forEach
遍历项目,并且对于每个项目,它检查是否存在从其id到空Optional
的映射,并将其替换为Optional
封装该项目,如果有这样的映射,则在一个操作中computeIfPresent
。
最后一个for
循环在ids
列表上向后迭代,因为您希望按顺序处理它们,并且如果存在非空Optional
则执行操作。由于地图已使用列表中的所有ID初始化,get
将永远不会返回null
,如果在{{1}中找不到ID,则会返回空Optional
} list。
这样,假设items
的查找具有Map
时间复杂度(在典型实现中就是这种情况),净时间复杂度从O(1)
变为{{1} } ...
答案 2 :(得分:2)
如果你想坚持使用流并向后迭代,你可以这样做:
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
}));
此代码与您的代码相同。
它从种子开始向后迭代:ids.size() - 1
。 int
的初始流的大小限制为limit()
,因此没有否定int
,且流与ids
列表的大小相同。然后,map()
操作将索引转换为id
列表中第i个位置的实际ids
(这是通过调用ids.get(i)
完成的)。最后,在items
列表中以与代码中相同的方式搜索项目。