【达内课程】集合之 HashMap

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);
    }

运行结果:
在这里插入图片描述

了解哈希算法

存放数据

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值