0
点赞
收藏
分享

微信扫一扫

【达内课程】集合之 HashMap


文章目录

  • ​​HashMap 介绍​​
  • ​​举例:HashMap 使用练习​​
  • ​​举例:统计字符串中字母个数​​
  • ​​了解哈希算法​​
  • ​​举例:哈希算法理解练习​​
  • ​​Java 遍历 Map 对象的四种方式​​

HashMap 介绍

介绍

HashMap 是一个散列表,它存储的内容是键值对 (key-value) 映射。每一个键值对也叫做 Entry。它的 键 是不重复且无序的。

HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。

HashMap 是无序的,即不会记录插入的顺序。

创建对象

HashMap map = new HashMap();

方法

方法

描述

​​clear()​​

删除 hashMap 中的所有键/值对

​​clone()​​

复制一份 hashMap

​​isEmpty()​​

判断 hashMap 是否为空

​​size()​​

计算 hashMap 中键/值对的数量

​​put()​​

将键/值对添加到 hashMap 中

​​putAll()​​

将所有键/值对添加到 hashMap 中

​​putIfAbsent()​​

如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。

​​remove()​​

删除 hashMap 中指定键 key 的映射关系

​​containsKey()​​

检查 hashMap 中是否存在指定的 key 对应的映射关系。

​​containsValue()​​

检查 hashMap 中是否存在指定的 value 对应的映射关系。

​​replace()​​

替换 hashMap 中是指定的 key 对应的 value。

​​replaceAll()​​

将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。

​​get()​​

获取指定 key 对应对 value

​​getOrDefault()​​

获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值

​​forEach()​​

对 hashMap 中的每个映射执行指定的操作。

​​entrySet()​​

返回 hashMap 中所有映射项的集合集合视图。

​​keySet​​()

返回 hashMap 中所有 key 组成的集合视图。

​​values()​​

返回 hashMap 中存在的所有 value 值。

​​merge()​​

添加键值对到 hashMap 中

​​compute()​​

对 hashMap 中指定 key 的值进行重新计算

​​computeIfAbsent()​​

对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中

​​computeIfPresent()​​

对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。

举例:HashMap 使用练习

HashMap<Integer,String> map = new HashMap<>();
map.put(9527,"Mr White");
map.put(9528,"PinkMan");
map.put(9529,"Goodman");
//添加重复的键,新的值会覆盖以前的值
map.put(9528,"Gale");
map.put(9530,null);
map.put(null,"---");
System.out.println(map.size());//输出 5
//数据显示无序
System.out.println(map);//{null=---, 9527=Mr White, 9528=Gale, 9529=Goodman, 9530=null}
System.out.println(map.get(9527));//Mr White
System.out.println(map.get(9999));//null
System.out.println(map.containsKey(9527));//true
System.out.println(map.containsValue("Gus"));//false
//返回被移除的值
System.out.println(map.remove(9529));//Goodman
System.out.println(map.remove(9999));//null

举例:统计字符串中字母个数

System.out.println("请输入字母:");
String s = new Scanner(System.in).nextLine();

/*
* 1.新建 HashMap<Character,Integer> 赋值给 map
* 2.循环字符串 s 从0到 <s.length()递增
* 3.从s取i位置字符赋值给c
* 4.从map取出字符串c对应的计数值
* 赋值给count
* 5.如果count==null
* 6.向map放入字符c和计数1
* 7.否则
* 8.向map放入字符c和计数值count+1
* 9.打印map
*/

HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
Integer count = map.get(c);

if (count == null) {
map.put(c, 1);
} else {
map.put(c, count + 1);
}
}
System.out.println(map);
}

运行结果:
【达内课程】集合之 HashMap_java

了解哈希算法

存放数据

1、key.hashCode() 获得哈希值
2、用哈希值计算下标值
3、创建 Entry 对象封装键和值
4、如果负载率达到75%,容量翻倍
5、Entry对象放入 i 位置
-------6、如果是空位置,直接放入
-------7、如果不是空位置
--------------8、依次用 equals() 比 较每个键是否相等
--------------9、找到相等的覆盖值

取出数据

1、key.hashCode() 获得哈希值
2、用哈希值计算下标值
3、依次 equals() 比较每个键是否相等
-------4、找到相等的,返回对应的值
-------5、没有找到相同的键,返回 null

举例:哈希算法理解练习

新建 Student 类

class Student {
private int id;
private String name;
private String gender;
private int age;

public Student() {

}

public Student(int id, String name, String gender, int age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}

public void setId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

java

Student s1 = new Student(5, "李小明", "男", 22);
Student s2 = new Student(5, "李小明", "男", 22);

//两个对象的哈希值相同
//才能计算出相同的下标位置
//新分配的内存地址作为哈希值,所以不同
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());

//即使hashCode()相等
//必须equals()也相等才能覆盖
System.out.println(s1.equals(s2));

HashMap<Student, Integer> map = new HashMap<>();
map.put(s1, 92);
map.put(s2, 98);
System.out.println(map);

输出结果

381259350
2129789493
false
{com.company.Student@7ef20235=98, com.company.Student@16b98e56=92}

对程序进行解释:

打印 map 时,自动调用类的​​toString()​​​方法,默认的是 类名+内存地址,可以在类中重写​​toString()​​方法。

​hashCode()​​:获得对象的哈希值。Object 中默认实现是: 使用内存地址值作为哈希值,如果需要,可以在子类中重写这个方法 ,使用属性数据来计算产生哈希值。

学生对象 s1 和 s2 是同一个人,后面的成绩应该覆盖前面的成绩,但是由于​​hashCode()​​不同,所以不会覆盖。这样实际上违背了我们的意图。因为我们在使用HashMap 时,希望利用相同内容的对象索引得到相同的目标对象,这就需要HashCode() 在此时能够返回相同的值。

要想用 s1 的值覆盖 s2 的值,不仅需要​​hashCode()​​​相等,​​equals()​​必须也相等才可以,所以在 Student 中重写这3个方法

Student类

class Student {
private int id;
private String name;
private String gender;
private int age;

public Student() {

}

public Student(int id, String name, String gender, int age) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
}

public void setId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String toString() {
return "\n学号:" + id +
"\n姓名:" + name +
"\n性别:" + gender +
"\n年龄:" + age;
}

public int hashCode() {

/* 让不同的属性值
* 尽量计算出不同的哈希值
*
* 有一种算法
* 是数学家发明的
* 是一种有效的惯用算法
*/

int p = 31;
int r = 1;
r = r * p + id;
r = r * p + name.hashCode();
r = r * p + gender.hashCode();
r = r * p + age;
return r;
}

public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof Student)) {
return false;
}
Student s = (Student) obj;
return id == s.id &&
age == s.age &&
name.equals(s.name) &&
gender.equals(s.gender);
}
}

重新运行程序:

-617173112
-617173112
true
{
学号:5
姓名:李小明
性别:男
年龄:22=98}

Java 遍历 Map 对象的四种方式

首先初始化一个 map 数据

Map<String, String> map = new HashMap<String, String>();
map.put("0", "毫米");
map.put("1", "厘米");
map.put("2", "分米");
map.put("3", "米");

期望遍历此 map,输出 key 或 value,例如:

key=0,value=毫米
key=1,value=厘米
key=2,value=分米
key=3,value=米

方式一:使用 entrySet

这是最常见的并且在大多数情况下也是最可取的遍历方式

在键值都需要时使用

for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key=" + entry.getKey() + ",value=" + entry.getValue());
}

方式二:在 for-each 循环中遍历 keys 或 values

在 for-each 循环中遍历 keys 或 values

如果只需要 map 中的键 或者 值,你可以通过 keySet 或 values 来实现遍历,而不是用 entrySet,该方法比 entrySet 遍历在性能上稍好(快了10%),而且代码更加干净

//遍历map中的键 
for (String key : map.keySet()) {
System.out.println("key=" + key);
}

//遍历map中的值
for (String value : map.values()) {
System.out.println("value=" + value);
}

方式三:使用 Iterator 遍历

该种方式看起来冗余却有其优点所在。首先,在老版本 java 中这是惟一遍历 map 的方式。另一个好处是,你可以在遍历时调用 ​​iterator.remove()​​来删除 entries,另两个方法则不能。根据 javadoc 的说明,如果在 for-each 遍历中尝试使用此方法,结果是不可预测的。

从性能方面看,该方法类同于for-each遍历(即方法二)的性能

--------使用泛型:

Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = entries.next();
System.out.println("key=" + entry.getKey() + ",value=" + entry.getValue());
}

--------不使用泛型:

Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
System.out.println("key=" + entry.getKey() + ",value=" + entry.getValue());
}

方式四:通过键找值遍历(效率低)

作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。所以尽量避免使用

for (String key : map.keySet()) {
String value = map.get(key);
System.out.println("key=" + key + ",value=" + value);
}

参考
​​​Java HashMap​​

举报

相关推荐

0 条评论