删除Map <Long,List <Object >>的条目

时间:2019-08-20 13:29:22

标签: java java-stream

经过数小时的研究,我找不到解决问题的方法。我有一个Map<String, List<User>>,我想根据列表中的某些值删除地图中的某些项目。

我必须遵循User class

public class User {
    public String firstName,
    public Integer age

    public boolean is50() {
       return this.age.equals(50);
    }

}

我有一个这样的地图:

Map<String, List<User> myMap = new HashMap<>();

myMap.put("A", new ArrayList<>());
myMap.get("A").add(new User("John", 50));
myMap.get("A").add(new User("Bob", 30));

myMap.put("B", new ArrayList<>());
myMap.get("B").add(new User("Sam", 25));
myMap.get("B").add(new User("Sarah", 56));

myMap.put("C", new ArrayList<>());
myMap.get("C").add(new User("Gill", 15));
myMap.get("C").add(new User("Jim", 20));

现在,如果列表年龄中至少有一个用户为50,我想删除Map的条目。此外,我想使用Java 8功能来实现。

我发现有一个removeIf函数,但是我不能使其与列表一起使用。

我尝试过这样的事情:

Map<String, List<User> filteredMap = myMap
                                 .enrySet()
                                 .removeIf(e -> e.getValue()
                                        .stream()
                                        .filter(user -> user::is50)
                                        .collect(Collectors.toList())

当然不行:(

例外的输出是一个过滤的地图,所有用户只有B键和C键(必须删除A键,因为John的年龄为50岁)

感谢您的帮助,希望一切都清楚;)

4 个答案:

答案 0 :(得分:8)

这应该可以解决问题:

myMap.entrySet()
    .removeIf(e -> e.getValue()
        .stream()
        .anyMatch(User::is50));

请注意,entrySet()的修改(removeIf)影响条目集支持的映射。因此,您必须制作地图的副本并在副本上运行它,或者修改第一个地图(myMap)。

要制作地图副本,您只需执行以下操作:

Map<String, List<User>> copy = new HashMap<>(myMap);

编辑:
按照@Holger的建议,执行就地删​​除的更好方法如下:

myMap.values()
    .removeIf(v -> v.stream()
        .anyMatch(User::is50));

这更好的原因有四个:

  1. 您可以消除对getValue的调用,因为values直接提供了设置的值。
  2. 它可以更清晰地传达意图-与values和一个值之间的逻辑跳跃相比,entrySet和一个值之间的逻辑跳跃要少。
  3. 更简洁并且不是反模式(例如,尽管有时更简洁,但在调试之外使用peek却是反模式)。
  4. 它仍然有效,因为values返回的集合是backed by the map

答案 1 :(得分:7)

只需将Stream::noneMatch用于嵌套流:

Map<String, List<User>> filteredMap = myMap.entrySet()
                .stream()
                .filter(e -> e.getValue()
                        .stream()
                        .noneMatch(User::is50)
                )
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

filteredMap.forEach((key, value) -> System.out.println(key));

输出为:

B
C

在此解决方案中,myMap不会受到影响,因为我使用了stream中的entrySet然后收集它。 Set::removeIf会更改您以前的地图,因为Map::entrySet返回了Set个条目,这些条目支持您的原始Map

答案 2 :(得分:2)

您已经注意到,您必须在EntrySet上进行操作。您可以将其转换为流,然后像这样收集回新地图:

myMap.entrySet().stream()
             .filter(e -> e.getValue().stream().noneMatch(User::is50))
             .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))

答案 3 :(得分:0)

您可以使用Map EntrySet来解析地图,然后解析每个条目中的列表

for (Map.Entry<String, List<User> entry : myMap.entrySet())
{
    boolean toDelete = false;
    for (User user : entry.getValue())
    {
        if (user.age == 50)
            toDelete = true;
    }
    if (toDelete)
        myMap.remove(entry.getKey());
}