ACAC HashMap概述(get(v)和put(k,V))

package HashMap;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;


/**
 *  HashMap集合:
 *    1、HashMap集合底层是哈希表/散列表数据结构
 *    2、哈希表是一个怎样的数据结构呢?
 *        哈希表是一个数组和单向链表的结合体
 *        数组:在查询方面效率很高,随机增删方面效率很低
 *        哈希表将以上的两种数据结构融合在一起,充分发挥他们各自的优点
 *
 *    3、HashMap集合底层的源代码:
 *
 *         public class HashMap<K,V>{
 *
 *         HashMap底层实际上是一个数组(一维数组)
 *         transient Node<K,V>[] table;

 *        静态内部类HashMap.Node
 *         static class Node<K,V> implements Map.Entry<K,V> {
 *         final int hash;
 *         哈希值(哈希值是key的hashCode()方法的执行结果,hash值通过哈希函数/算法,可以转换成数组的下标
 *         final K key;
 *         V value;
 *         Node<K,V> next;
 *         }
 *      }
 *      哈希表/数列表:一位数组,这个数组中每一个元素是一个单向链表(数组和链表的结合体)
 *
 *    4、HashMap集合的Key部分特点:
 *          无序,不可重复
 *          为什么无序?因为不一定挂到哪个链表下(取决于经过HashCode()方法算的哈希值)
 *          不可重复怎么保证?因为挂的时候肯定是不重复的,用equals方法进行
 *          如果key重复了,value会覆盖(比如要添加一个(1,aa),在添加一个(1,bb),则bb把aa替换掉
 *              但是value可以重复,如添加一个(2,bb)是可以的,(就相当于不同保险柜里的钱可以一样,
 *              但密码不能一样,一样的话默认是一个保险柜,并更新里面的钱)
 *
 *         放在HashMap集合Key部分的元素其实就是放到HashSet集合中
 *         所以HashSet集合中的元素也需要同时重写hashCode() + equals()方法
 *         
 *    5、哈希表HashMap使用不当时无法发挥性能:
 *       假设将所有的hashCode()方法返回值固定的话,则HashMap表将会变成纯单链表
 *       这种情况称为:散列分步不均匀
 *       什么是散列分布均匀?
 *          100个元素,10个单向链表,每个单向链表上有10个节点,则散列分布均匀
 *       假设将所有的HashCode()方法返回值都设定为不一样的值,可以吗?有什么问题?
 *          不行,都不一样的话,就成数组了,就挂不上去了
 *          也是散列分布不均匀
 *       散列分布均匀:需要重写hashCode()方法时,有一定技巧。
 *
 *      6、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法
 *
 *      7.HashMap集合默认初始化容量为16,默认加载因子是0.75
 *        这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容
 *
 *   重点:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,因为达到散列均匀
 *        为了提高HashMap集合存取效率所必须的
 *
 *
 */

public class Note01 {
    public static void main(String[] args) {

        //Integer是key,他的hashCode和equals方法都重写了
        Map<Integer,String> map = new HashMap<>();
        map.put(111,"ha");
        map.put(222,"zhu");
        map.put(333,"chen");
        map.put(111,"luo");

        System.out.println(map.size());

        Set<Map.Entry<Integer,String>> it = map.entrySet();

        for(Map.Entry<Integer,String> node : it){
            System.out.println("key : " + node.getKey() + " value : " + node.getValue());
        }

        /**
         * 验证结果:无序不可重复
         * key : 333 value : chen
         * key : 222 value : zhu
         * key : 111 value : luo
         */




    }

}

package HashMap;

//本来还认为,Student stu  = "ss" 会不会是true,编译报错,
//相当于Student ss = 11;两者不是同一类型的
/**
 * 向Map集合中存,和取的时候:
 *      1、都是先调用key的hashCode方法,然后在调用equals方法
 *   equals方法也可能调用,也可能不调用:
 *      1、当put(k,V)的时候,K的hashCode()方法返回哈希值
 *         哈希值经过哈希算法转换成数组下标
 *         数组下标位置上如果是null(这个串是空的),equals方法不会调用
 *      2、get(K)的时候
 *         K的hashCode()方法返回哈希值
 *         哈希值经过哈希算法转换成数组下标
 *         数组下标位置上如果是null(这个串是空的),equals方法不会调用
 *       注意:只要串上有元素,就会调用equals方法比较,
 *
 * 注意:如果一个类的equals方法重写了,hashCode方法必须重写
 *      并且equals()方法返回如果是true(代表在同一个链表上,也就是串上),hashCode()方法返回的值必须一样
 *
 *      equals方法返回true,代表两个对象相同,在同一链表上比较,对于同一链表上的节点来说,哈希值是相同的
 *      所以hashCode()方法的返回值也应该相同
 *
 * 结论:放在HashMap集合Key部分,和放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法
 *      不建议自己写,直接系统生成就行
 */

import java.util.*;

public class Test01 {
    public static void main(String[] args) {

        //不重写equals的话,就是false,比较两个对象的内存地址,重写后是true
        Student stu = new Student("sss");
        Student stu01 = new Student("sss");
        System.out.println(stu.equals(stu01));

        System.out.println("s1的hashCode : " + stu.hashCode());//s1的hashCode : 2129789493
        System.out.println("s2的hashCode : " + stu01.hashCode());//s2的hashCode : 1313922862

        /**
         * s1.equals(s2)结果已经是true了,表示s1和s2是一样的,那么往HashSet集合中放的话
         * 按说只能放进去一个,HashSet集合特点:无序不可重复
         *
         *
         */
        Set<Construct> ss = new HashSet<>();

        System.out.println("按说是1,重写后,但结果是 :" + ss.size());//应该是1,但是没有重写hashCode方法就是2



    }

}
class Student{

    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public boolean equals(Object obj){
        if(obj == null || !(obj instanceof Student)) return false;
        if(obj == this) return true;
        Student stu = (Student)obj;
        return stu.name.equals(name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

package HashMap;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 *  这里稍微修改写系统hashCode和equals方法,让编号和类型进行比较
 */
public class Test02 {
    
    
    public static void main(String[] args) {
    
    

        Construct cc = new Construct("0x1111","fanqie",3.22);
        Construct dd = new Construct("0x1111","fanqie",3.23);
        Construct ee = new Construct("0x1111","fanqie",3.24);
        Construct ff = new Construct("0x1111","fanqie",3.23);


        Set<Construct> st = new HashSet<>();
        st.add(cc);
        st.add(dd);
        st.add(ee);
        st.add(ff);

        System.out.println("直接输出st存的对象 :" + st);
        System.out.println("重写后的: " + st.size());



        for(Construct data : st){
    
    
            System.out.println("类型key :" + data.leiXing + " 编号:" + data.bianHao + " 价格 :" + data.prize);
        }

        Set<String>  aa = new HashSet<>();
        aa.add("afjda");
        aa.add("sadfi");
        aa.add("sadfs");
        System.out.println("输出aa :  " + aa);

        /**
         *  重点:  无序,不可重复。。。。。。。
         *  输出结果:
         *      重写后的: 1
         *      类型key :fanqie 编号:0x1111 价格 :2.32
         * 结论:没有将prize放在hashCode和equals中,则prize就不会被覆盖
         *
         *  经过搜索:
         *      HashSet的确是包装了的HashMap
         *
         *      添加元素时,就是将对象e(这里是Construct对象)当成了key,调用HashMap的put方法
         *      public boolean add(E e) {
         *         return map.put(e, PRESENT)==null;
         *     }
         *
         *      而这个value,是new了一个object对象出来
         *      private static final Object PRESENT = new Object();
         *
         *      所有元素的value都指向Object对象,
         *      HashSet虽然底层是用HashMap来实现的,但由于用不到HashMap的value,
         *      所以不会为底层HashMap的每个value分配一个内存空间,因此并不会过多的占用内存
         *
         *
         */

    }
}
class Construct{
    
    

    public String bianHao;

    public String leiXing;

    public double prize;

    public Construct(String bianHao, String leiXing, double prize) {
    
    
        this.bianHao = bianHao;
        this.leiXing = leiXing;
        this.prize = prize;
    }

    public Construct() {
    
    
    }

    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Construct construct = (Construct) o;
        return Double.compare(construct.prize, prize) == 0 &&
                Objects.equals(bianHao, construct.bianHao) &&
                Objects.equals(leiXing, construct.leiXing);
    }

    @Override
    public int hashCode() {
    
    
        return Objects.hash(bianHao, leiXing, prize);
    }

    @Override
    public String toString() {
    
    
        return this.bianHao + " " + this.leiXing + " " + this.prize;
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44707513/article/details/110352929