如何优雅的删除HashMap元素

文章目录

  • 1.数据准备
  • 2.删除方式
    • [2.1.使用增强 for 循环删除](#2.1.使用增强 for 循环删除)
    • [2.2.使用 forEach 循环删除](#2.2.使用 forEach 循环删除)
    • [2.3.使用 Iterator 迭代器删除](#2.3.使用 Iterator 迭代器删除)
    • [2.4 使用 removeIf 删除(推荐使用)](#2.4 使用 removeIf 删除(推荐使用))
    • [2.5.使用 Stream 删除(推荐使用)](#2.5.使用 Stream 删除(推荐使用))

1.数据准备

java 复制代码
public Map<String, String> initMap = new HashMap<String, String>() {{
    put("user1", "刘零");
    put("user2", "郑一");
    put("user3", "吴二");
    put("user4", "张三");
    put("user5", "李四");
    put("user6", "王五");
    put("user7", "钱六");
    put("user8", "孙七");
}};

2.删除方式

2.1.使用增强 for 循环删除

java 复制代码
/**
 * 使用 for 循环删除
 */
public void remove1() {
    Set<Map.Entry<String, String>> entries = new CopyOnWriteArraySet<>(initMap.entrySet());
    for (Map.Entry<String, String> entry : entries) {
        if ("王五".equals(entry.getValue())) {
            initMap.remove(entry.getKey());
        }
    }
    System.out.println(initMap);
}

输出结果:

{user1=刘零, user2=郑一, user7=钱六, user8=孙七, user5=李四, user3=吴二, user4=张三}

通过HashMap的entrySet方法获取元素集合,然后再进行循环遍历,判断value值是否为需要删除的元素,再移除对应的Key。

需要注意增强的 for 循环底层使用的迭代器 Iterator,而 HashMap 是 fail-fast 原则的错误机制,所以遍历时删除元素会出现 java.util.ConcurrentModificationException 并发修改异常。我们可以使用CopyOnWriteArraySet封装一层避免出现并发修改异常。

  • fail-fast:为了将错误或异常情况尽早暴露出来,避免潜在的问题在后续代码中蔓延,提高系统的稳定性和可靠性。

2.2.使用 forEach 循环删除

java 复制代码
/**
 * 使用 forEach 循环删除
 */
public void remove2() {
    ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(initMap);
    map.forEach((k, v) -> {
        if ("王五".equals(v)) {
            map.remove(k);
        }
    });
    System.out.println(map);
}

输出结果:

{user1=刘零, user2=郑一, user7=钱六, user8=孙七, user5=李四, user3=吴二, user4=张三}

通过HashMap的forEach方法循环删除目标元素,同样的使用了ConcurrentHashMap封装避免出现并发修改异常。

2.3.使用 Iterator 迭代器删除

java 复制代码
/**
 * 使用 Iterator 迭代器删除
 */
@Test
public void remove3() {
    ConcurrentHashMap<String, String> resultMap = new ConcurrentHashMap<>();
    Iterator<Map.Entry<String, String>> iterator
            = new ConcurrentHashMap<>(initMap).entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        if ("王五".equals(entry.getValue())) {
            iterator.remove();
        }else {
            resultMap.put(entry.getKey(),entry.getValue());
        }
    }
    System.out.println(resultMap);
}

输出结果:

{user1=刘零, user2=郑一, user7=钱六, user8=孙七, user5=李四, user3=吴二, user4=张三}

通过Iterator迭代删除元素不会出现并发修改异常,但由于HashMap是线程不安全的,这时如果多个线程同时修改HashMap数据也会出现并发修改异常 ,日常使用可以先用ConcurrentHashMap封装。

2.4 使用 removeIf 删除(推荐使用)

java 复制代码
/**
 * 使用 removeIf 删除
 */
public void remove4() {
    initMap.entrySet().removeIf(entry -> "王五".equals(entry.getValue()));
    System.out.println(initMap);
}

输出结果:

{user1=刘零, user2=郑一, user7=钱六, user8=孙七, user5=李四, user3=吴二, user4=张三}

通过entrySet获取元素然后使用removeIf方法删除目标数据;而removeIf的底层是通过Iterator迭代器实现的。所以也存在第三种方法同样的问题。

java 复制代码
default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

2.5.使用 Stream 删除(推荐使用)

java 复制代码
/**
 * 使用 Stream 删除
 */
public void remove5() {
    Map<String, String> map = initMap.entrySet().stream()
            .filter(entry -> !"王五".equals(entry.getValue()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    System.out.println(map);
}

输出结果:

{user1=刘零, user2=郑一, user7=钱六, user8=孙七, user5=李四, user3=吴二, user4=张三}

通过Stream 的 filter 方法进行过滤,然后生成一个新的map。这种方式"一行代码"就能够实现删除的动作,并且没有并发问题。

相关推荐
drebander2 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天2496 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn11 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟12 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy21 分钟前
高级编程之结构化代码
java·spring boot·spring cloud
新知图书23 分钟前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
威威猫的栗子25 分钟前
Python Turtle召唤童年:喜羊羊与灰太狼之懒羊羊绘画
开发语言·python
力透键背25 分钟前
display: none和visibility: hidden的区别
开发语言·前端·javascript
bluefox197926 分钟前
使用 Oracle.DataAccess.Client 驱动 和 OleDB 调用Oracle 函数的区别
开发语言·c#
弗锐土豆28 分钟前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部