zoukankan      html  css  js  c++  java
  • 【Java】Map杂谈,hashcode()、equals()、HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap

    参考的优秀文章:

    《Java编程思想》第四版

    《Effective Java》第二版

    Map接口是映射表的结构,维护键对象与值对象的对应关系,称键值对

    > hashcode()和equals()

    hashcode()和equals()即用于识别对象的身份

    在HashMap或类似的实现中,查找一个对象,是通过hashcode()返回的散列值映射到一个范围内的下标,在通过equals()比较此下标连接的链表是否存在相同的对象。

    简单来说,hashcode()用于参考、快速定位(缩减范围),真正是否等于是依赖equals()。

    默认的hashcode()equals()

    如何对象没有覆盖这两个方法,那么就是继承Object对象的。

    Object中,hashcode()是使用对象的地址计算散列值;equals()只比较对象的地址

    必要的时候,我们需要覆盖这两个方法。

    覆盖这两个方法有什么原则呢?

    equals()的覆盖,主要是基于此对象的业务。

    而hashcode()的覆盖原则,详情可参见《Effective Java》的“覆盖equals时总要覆盖hashcode”一节。

    有几个比较重要的原则:

    1、两个equals相等的对象,其hashcode是相等的。

    2、两个equals不等的对象,其hashcode有可能是相等的。

    3、好的hashcode()应产生分布均匀的散列码。

    基于第3点,《Effective Java》有具体的建议。

    1、定义变量result为非零的数。

    2、用公式result = 31 * result + c,其中c是类中各个域的散列值。

    用Eclipse生成的hashcode()与此原则类似,我们可以看看:

    public class User {
        
        private Integer id;
        private String name;
        private boolean flag = false;
        private long phoneNumber;
        
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + (flag ? 1231 : 1237);
            result = prime * result + ((id == null) ? 0 : id.hashCode());
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            result = prime * result + (int) (phoneNumber ^ (phoneNumber >>> 32));
            return result;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            User other = (User) obj;
            if (flag != other.flag)
                return false;
            if (id == null) {
                if (other.id != null)
                    return false;
            } else if (!id.equals(other.id))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            if (phoneNumber != other.phoneNumber)
                return false;
            return true;
        }
        
            
    
    }
    View Code

    > 常用的Map实现

    Map接口有几个常用的实现类,HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap。其中HashMap最常用。

    HashMap,基于散列表实现,查找速度快(依赖hashcode()和equals()),存放元素无序。

    TreeMap,基于红黑树实现,存放有序(依赖Compareable)。

    LinkedHashMap,基于散列表、双向链表实现。如HashMap的查找速度,遍历时有序(默认为插入顺序,可通过构造方法设置“最近最少使用(Least Recently Used)顺序”)。

    ConcurrentHashMap,线程安全的HashMap,用于替代HashTable(线程安全,但基于整个对象的锁实现的,效率不高),而ConcurrentHashMap是采用分段加锁的方式实现,提升了效率。

    代码演示LinkedHashMap的两种排序:

    import java.util.LinkedHashMap;
    import java.util.Map;
    
    
    public class LinkedHashMapTester {
    
        public static void main(String[] args) {
    
            System.out.println("LinkedHashMap 根据插入顺序排列:");
            Map<String, String> map = new LinkedHashMap<String, String>();
            for (Integer i = 0; i < 5; i++) {
                map.put("k" + i.toString(), "v" + i.toString());
            }
            
            for (Integer i = 10; i > 5; i--) {
                map.put("k" + i.toString(), "v" + i.toString());
            }
            
            System.out.println(map);
            map.get("k10");
            System.out.println(map);
            
            System.out.println("LinkedHashMap 根据最近最少使用(Least Recently Used)顺序排列:");
            map = new LinkedHashMap<String, String>(16, 0.75f, true); // 没有了其他构造方法设置其排序为true了。初始容量、加载因子使用默认的16和0.75。
            for (Integer i = 0; i < 5; i++) {
                map.put("k" + i.toString(), "v" + i.toString());
            }
            
            for (Integer i = 10; i > 5; i--) {
                map.put("k" + i.toString(), "v" + i.toString());
            }
            
            System.out.println(map);
            map.get("k10");
            System.out.println(map);
        }
    
    }
    View Code

    日志,注意看使用“K10”后“K10”的位置:

    LinkedHashMap 根据插入顺序排列:
    {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6}
    {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6}
    LinkedHashMap 根据最近最少使用(Least Recently Used)顺序排列:
    {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6}
    {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k9=v9, k8=v8, k7=v7, k6=v6, k10=v10}
    View Code
  • 相关阅读:
    2015306 白皎 《网络攻防》Exp5 MSF基础应用
    2015306 白皎 《网络攻防》Exp4 恶意代码分析
    2015306 白皎 《网络攻防》Exp3 免杀原理与实践
    2015306 白皎 《网络攻防》Exp1 进阶
    20155306 白皎 《网络攻防》 Exp2 后门原理与实践
    20155306 白皎 《网络攻防》Exp1 PC平台逆向破解——逆向与Bof基础
    2017-2018-1 20155306 《信息安全系统设计基础》嵌入式C语言———提取设置时分秒
    2017-2018-1 20155306 《信息安全系统设计基础》课程总结
    2017-2018-1 20155306 《信息安全系统设计基础》第十四周学习总结
    2017-2018-1 20155306 《信息安全系统设计基础》第13周学习总结
  • 原文地址:https://www.cnblogs.com/nick-huang/p/5487806.html
Copyright © 2011-2022 走看看