繁体   English   中英

Java 8 Stream在列表中查找元素

[英]Java 8 Stream to find element in list

我有以下课程:

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,这应该摆脱异常。 谢谢。

您可以尝试使用以下内容:

ids.forEach(id -> 
    list.stream()
    .filter(p -> p.getId() == id)
    .findFirst()
    .ifPresent(p -> {
        // do stuff here
    });
);

这里的可选显示您的过滤器方法可以返回空流,因此如果您调用findFirst,它可以找到一个或零个元素。

如果您有很多要搜索的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 ,如果在items列表中找不到id,它将返回空的Optional

这样,假设Map的查找具有O(1)时间复杂度,这是典型实现中的情况,净时间复杂度从O(m×n)变为O(m+n) ......

如果你想坚持使用流并向后迭代,你可以这样做:

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()操作将索引转换为位于ids列表中第i个位置的实际id (这通过调用ids.get(i) )。 最后,在items列表中以与代码中相同的方式搜索items

您想为每个给定的ID找到最多一个项目,并对找到的项目执行某些操作,对吗? 更多性能改进:

Set<Integer> idsToLookup = new HashSet<>(getIdsToLookup()); // replace list with Set

items.stream()
    .filter(e -> idsToLookup.remove(e.getId()))
    .forEach(
       /* doing something */
     );

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM