首页 > 编程语言 >详解Java HashMap

详解Java HashMap

时间:2023-10-16 18:11:28浏览次数:35  
标签:map 遍历 Java HashMap System 详解 println entry out

HashMap介绍

HashMap遍历方式

HashMap的遍历,大体上可分为4类,而每种类型下又有不同的实现方式,总共的遍历方式可分为7种:

  1. 迭代器遍历:
    • 使用迭代器对EntrySet遍历;
    • 使用迭代器对KeySet遍历;
  2. foreach遍历:
    • 使用foreach对EntrySet遍历;
    • 使用foreach对KeySet遍历;
  3. lambda表达式遍历;
  4. streams API遍历:
    • Streams API单线程方式遍历;
    • Streams API多线程方式遍历。

下面列举出几种遍历方式,首先创建并初始化一个HashMap:

public class HashMapTest {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "Python");
        map.put(3, "C++");
        map.put(4, "Rust");
        map.put(5, "Go");

        System.out.println("1. 迭代器遍历EntrySet");
        traverseByIterator1(map);
        System.out.println("2. 迭代器遍历KeySet");
        traverseByIterator2(map);
        System.out.println("3. foreach遍历EntrySet");
        traverseByForeach1(map);
        System.out.println("4. foreach遍历KeySet");
        traverseByForeach2(map);
        System.out.println("5. lambda表达式遍历");
        traverseByLambda(map);
        System.out.println("6. streams API单线程");
        traverseByStreams1(map);
        System.out.println("7. streams API多线程");
        traverseByStreams2(map);
    }
}

迭代器

EntrySet

public static void traverseByIterator1(Map map) {
	Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
	while (iterator.hasNext()) {
		Map.Entry<Integer, String> entry = iterator.next();
		System.out.println(entry.getKey() + " : " + entry.getValue());
	}
}

输出:

1. 迭代器遍历EntrySet
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

KeySet

public static void traverseByIterator2(Map map) {
	Iterator<Integer> iterator = map.keySet().iterator();
	while (iterator.hasNext()) {
		Integer key = iterator.next();
		System.out.println(key + " : " + map.get(key));
	}
}

输出:

2. 迭代器遍历KeySet
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

foreach

对于foreach遍历,内部也是通过创建迭代器来遍历

EntrySet

public static void traverseByForeach1(Map<Integer, String> map) {
	for (Map.Entry<Integer, String> entry : map.entrySet()) {
		System.out.println(entry.getKey() + " : " + entry.getValue());
	}
}

输出:

3. foreach遍历EntrySet
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

KeySet

public static void traverseByForeach2(Map<Integer, String> map) {
	for (Integer key : map.keySet()) {
		System.out.println(key + " : " + map.get(key));
	}
}

输出:

4. foreach遍历KeySet
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

lambda表达式

public static void traverseByLambda(Map map) {
	map.forEach((key, value) -> {
		System.out.println(key + " : " + value);
	});
}

输出:

5. lambda表达式遍历
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

streams API

单线程

public static void traverseByStreams1(Map<Integer, String> map) {
	map.entrySet().stream().forEach((entry) -> {
		System.out.println(entry.getKey() + " : " + entry.getValue());
	});
}

输出:

6. streams API单线程
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

多线程

public static void traverseByStreams2(Map<Integer, String> map) {
	map.entrySet().parallelStream().forEach((entry) -> {
		System.out.println(entry.getKey() + " : " + entry.getValue());
	});
}

输出:

7. streams API多线程
1 : Java
2 : Python
3 : C++
4 : Rust
5 : Go

遍历时删除元素

在上述几种遍历方式中,有些可以在遍历过程中安全删除元素,有些则会抛出ConcurrentModificationException异常,这是因为遍历过程中会比较modCount != expectedModCount,不相等就会抛出异常,具体分析请往下看。

迭代器

Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry<Integer, String> entry = iterator.next();
	if (entry.getKey() == 1) {
		iterator.remove();
	}
}

通过迭代器遍历,并使用迭代器的remove()方法可以正常删除元素。
成功的原因是,调用iterator.remove()方法最后会对expectedModCount值进行更新,这样就保证了迭代器调用next()获取下一个元素时,检查modCount == expectedModCount
image

foreach

for (Map.Entry<Integer, String> entry : map.entrySet()) {
	if (entry.getKey() == 2) {
		map.remove(entry.getKey());
	}
}

在foreach遍历的过程中调用Map的remove()方法会抛出ConcurrentModificationException异常。
通过查看源码得知,抛出异常的原因是删除元素之后,进行下一个元素的遍历时,比较变量modCountexpectedModCount不相等:
image
HashMap中的变量modCount记录了HashMap的修改次数,HashIterator中的变量expectedModCount在遍历前会初始化与modCount相等,当删除一个元素时,++modCount,之后迭代器通过next()获取下一个元素时,检查modCount != expectedModCount,就会抛出异常。
此外,这种检查到错误就抛出异常并停止程序后续执行的机制被称为fail-fast机制。

lambda

map.forEach((key, value) -> {
	if (key == 1) {
		map.remove(key);
	}
});

使用lambda表达式遍历时删除也会抛出ConcurrentModificationException。

可以通过removeIf()对key进行判断后删除。

map.keySet().removeIf(key -> key == 1);
map.forEach((key, value) -> {
	System.out.println(key + value);
});

sterams:

map.entrySet().stream().forEach((entry) -> {
	if (entry.getKey() == 1) {
		map.remove(entry.getKey());
	}
});

使用stream遍历删除同样抛出ConcurrentModificationException。
可以使用filter()过滤掉不需要的数据再遍历,但是这种方式不会真正删除hashmap中的元素。

map.entrySet().stream().filter(e -> 1 != e.getKey()).forEach((entry) -> {
	if (entry.getKey() == 1) {
		System.out.println(entry.getKey());
	}
});

HashMap扩容机制

HashMap线程安全问题

标签:map,遍历,Java,HashMap,System,详解,println,entry,out
From: https://www.cnblogs.com/KRDecad3/p/17768033.html

相关文章

  • java程序的运行流程(jdk,jre,jvm的关系)
    资料来源于第8课:https://www.bilibili.com/video/BV1o841187iP?p=8&spm_id_from=pageDriver&vd_source=c3a656550cf5d38944e8878bb7026cbc1.我们手写的java 代码为.java后缀文件储存在src当中。2.经过jdk中的bin中的javac.exe文件编译出.class文件的字节码。3.字节码储存在ou......
  • java中接口的实现方式
    目录Java8接口初始化的几种场景通过接口实现类的方式实现1.定义接口2.接口实现3.测试方法通过匿名内部类的来实现接口实现3.测试方法通过方法的引用1.实现通过箭头函数Lambda表达式的方式1.定义接口2.接口实现3.测试方法将接口作为方法参数1.定义一个方法2.调用方法并......
  • Java 中的异常处理
    在Java中,异常是中断程序正常流程的事件。当发生意外情况时,会引发异常。如果没有正确的处理,这些异常可能会使您的程序崩溃。Java提供了强大的异常处理机制,确保您的代码能够优雅地处理意外情况。异常可能由多种因素引起,例如:用户输入错误硬件故障网络连接错误数据库错误编程......
  • Java拾贝第三天——面向对象2
    继承性面向对象三大特性:封装性,继承性,多态性。继承的作用是避免出现大幅度的相同代码,提高代码的复用性。//现有一个Person类,拥有name,age属性classPerson{privateStringname;privateintage;//构造方法和setter,getter现在有一个Student类,它和Person类的属性......
  • Java Assert断言使用
    目录断言所谓的assertion,是jdk1.4后加入的新功能。作用它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。后续当软件正式发布后,可以取消断言部分的代码。java中使用assert作为断言的一个关......
  • java web
    0.了解maven1.了解http协议2.了解tmcat的作用3.了解请求响应4.了解分层解耦5.了解servlet原理Maven为java世界引入了一个新的依赖管理系统jar包管理jar包升级时修改配置文件即可......
  • Java设计模式
    七大设计原则开闭原则:是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭依赖倒置原则:是指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象而不依赖于具体。单一职责原则:是指一个Class/Interface/Method只负责一项职责。接口隔离原则:是指用多个专......
  • JS实现导出Excel的五种方法详解
    <!DOCTYPEhtml><html><headlang="en"><metacharset="UTF-8"><title>html表格导出道</title><scriptlanguage="JavaScript"type="text/javascript">//第一种方法function......
  • Java常见集合类学习笔记
    List1.ArrayListVectorLinkedList区别​ ArrayList和Vector底层实现基本相同,都是基于数组实现的,只是Vector的方法用synchronized修饰;所以ArrayList是线程不安全的,Vector是线程安全的。​ LinkedList底层基于双向链表实现,方法没有用synchronized修饰,线程不安全。2.数组和......
  • Java大文件上传详解及实例代码
    前言:上周遇到这样一个问题,客户上传高清视频(1G以上)的时候上传失败。一开始以为是session过期或者文件大小受系统限制,导致的错误。查看了系统的配置文件没有看到文件大小限制,web.xml中seesiontimeout是30,我把它改成了120。但还是不行,有时候10分钟就崩了。同事说,可能是客户这里服......