我有一个包含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#limit
、Collectors#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]