Java流映射过滤器,删除和收集



我有一个包含1000个项目的Map,我想从该Map的前500个项目中生成一个List,并删除从Map中收集的项目。

换句话说,我想过滤、收集到List并从Map中删除项目。

我正在尝试类似的东西:

final int i = 0;
int max = 5;
Map<String, Object> map = new HashMap<>();
map.put("ads", "123");
map.put("qwe", "123");
map.put("cvb", "123");
map.put("asd", "123");
map.put("iop", "123");
map.put("jkl", "123");
map.put("yui", "123");
List list = map.entrySet().stream().filter(y -> i++ < max).collect(Collectors.toList());

预期输出:

具有2个值的地图

列出5个值

Stream API旨在创建一个新集合,但不更改现有集合,从中创建流。

在这种情况下最好使用迭代器功能。像这样:

final int MAX = 500;
Map<String, String> map = new HashMap<>();
map.put("Item1", "Item 1 value");
map.put("Item2", "Item 2 value");
// ...
map.put("Item1000", "Item 1000 value");
List<String> list = new ArrayList<>();

var iterator = map.entrySet().iterator();
for (int i = 0; i< MAX; i++) {
if (iterator.hasNext()) {
var item = iterator.next();
list.add(item.getValue());
iterator.remove();
}
}

正如其他答案和注释中所指出的,在流操作期间,您不能更改映射。所以这需要两个步骤。

var list = map.keySet().stream().limit(500).toList();
map.keySet().removeAll(list);

我在这里使用了Java16neneneba API,但以前的版本只是小的变化。

然而,请注意,如果没有定义映射的顺序,那么这是一件非常没有意义的事情。如果使用带有谓词的过滤器,则会更有意义。尽管在这种情况下,一个更可读的替代方案可能是:

var list = map.keySet().stream().filter(predicate).toList();
map.keySet().removeIf(predicate);

使用单流实现这一点的最简单方法是使用Stream#limitCollectors#collectingAndThen并在第二个下游Collector内执行removeAll

这是可能的,因为第一个Collectors.toList将消除映射的迭代,然后第二个操作可以删除而不会得到异常

List<Map.Entry<String, Object>> list = map.entrySet()
.stream()
.limit(max)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
l -> {
map.entrySet().removeAll(l);
return l;
}
));
System.out.println("map = " + map); // map = {qwe=123, cvb=123}
System.out.println("list = " + list); // list = [ads=123, asd=123, jkl=123, iop=123, yui=123]

最新更新