HashMap和HashSet

1.和Java的关系 

  1. HashMap HashSet java 中利用哈希表实现的 Map Set.
  2. java   会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树.
  3. java 中使用的是哈希桶方式解决冲突的.
  4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key equals 法。所以如果要用自定义类作为 HashMap key 或者 HashSet 的值,必须覆写 hashCode equals ,而且要做到 equals 相等的对象,hashCode 一定是一致的。

2.相关代码案例


public class HashBuck {
    //此内部类描述哈希桶的节点
    static class Node {
        public int key;
        public int val;
        public Node next;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;

        }
    }



    public Node[] array;
    public int usedSize;

    //  定义一个默认的负载因子为0.75
    public static final double LOAD_FACTER = 0.75f;

    public HashBuck( ) {

        array = new Node[10];
    }
    public void put(int key, int val) {
        /**
         * 给定一个 key 用key 去模数组的长度 得到一个值给到index
         *  index为 数组的下标 数组里的每一个元素又分为一个链表
         *  每个链表有三个节点 key  val  和 next
         */

        int index = key % array.length;
        Node cur = array[index];
        /**
         * cur为链表头节点 如果这个链表不为空
         * 如果给定的 key 等于 cur的key 就把val 给到cur的val 如果不等于 cur 往后走
         * 如果在 cur链表中找不到给定的key 就把给定的 key val 采用头插法或尾插法  此处用头插法
         */

        while(cur != null) {
            if(cur.key == key) {
                cur.val = val;
                return;
            }
            cur = cur.next;
        }
        /**
         * cur走到空了 说明当前链表中没有给定的key val  就头插
         * 头插 先新建一个节点node   先该节点(node)的next指向原来链表的头结点  再将该节点放在array[index]充当头节点
         * 有效长度加1 (插入一个usedsize++)
         */

        Node node = new Node(key,val);
        node.next = array[index];
        array[index] = node;
        usedSize++;
        /**
         *  usedSize++ 后要判断此时的负载因子是否大于默认的负载因子
         *  如果大于就要扩容了
         */
        if(calculateLoadFactor() >= LOAD_FACTER ) {
            // 扩容 通过resize方法扩容
            resize();
        }


    }

    /**
     * 扩容一定要注意
     * 每一个元素都要进行重新的哈希计算
     * 因为它们可能不在原来的位置了
     * 链表长度> = 8 && 数组长度>= 64 此时会变成红黑树(以后在看)
     */
    private void resize() {
        // 新的数组是旧的数组的2倍
        Node[] newArray = new Node[2* array.length];
        // 因为array数组的每一个下标都是一个链表
        // 要想遍历每一个元素 先遍历数组 通过cur来指向每一个链表 通过while循环来遍历链表 然后全都放到newArray中
        for(int i = 0;i< array.length;i++) {
            Node cur = array[i];
            while(cur != null) {
                Node curNext = cur.next; //用curNext来记录下来cur.next
                int index = cur.key % newArray.length;// 找到cur 的关键字在新数组的位置
                cur.next = newArray[index]; // 因为cur指代的是链表 数组变化了  cur.next也要指向新的位置 头插
                newArray[index] = cur; //头插
                cur = curNext;//cur往后走 遍历链表的后一个...
            }
        }
        // 链表 和数组改完后将新的数组给到array array就导表newArray
        array = newArray;

    }


    // 计算负载因子
    private double calculateLoadFactor() {
        return usedSize*1.0 / array.length;
    }

    // get方法来获取到 key 对应的val值
    public int get(int key) {
        int index = key % array.length;
        Node cur = array[index];
        while(cur != null) {
            if(cur.key == key) {
                return cur.val;
            }
            cur = cur.next;
        }
        return -1;
    }


}
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LIUQIANG
 * Date: 2023-05-09
 * Time: 10:42
 */

class Student {
    private String id;

    public Student(String id) { //构造方法
        this.id = id;
    }

    //要想hashCode在同一个(例如"1234")下生成的哈希码一样 要以自己的方式去重写hashCode equals也一样


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id);
    }

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

    @Override
    public String toString() { //toString方法
        return "student{" +
                "id='" + id + '\'' +
                '}';
    }
}







public class Test {
    public static void main(String[] args) {
        /**
         * 统计10w个数据 求重复数据出现的次数  K-V模型
         * 做法: 遍历数组: x -->map有 val+1 没有 val就是1
         */
        int[] array = {1,2,2,3,4,5,6,8,4};
        Map<Integer,Integer> map = new HashMap<>();
        for (int x:array) {
            if(map.get(x) == null) {
                map.put(x,1);
            } else {
                int val = map.get(x);
                map.put(x,val+1);
            }
        }
        // map.entrySet() 是遍历map的一种方法
        for (Map.Entry<Integer,Integer> entry: map.entrySet()  ) {
            if(entry.getValue() > 1) {
                System.out.println("key: "+entry.getKey()+" val: "+entry.getValue() );
            }
        }
    }
    public static void main5(String[] args) {
        int[] array = {1,2,2,3,4,5,6,8,4};
        Set<Integer> set = new HashSet<>();
        for (int x:array) {
            //set用来去重
            //  找到第一个重复数据,返回第一个重复元素
            if(!set.contains(x)) {
                set.add(x);
            } else {
                System.out.println(x);
                return;
            }

        }
    }
    public static void main4(String[] args) {
        int[] array = {1,2,2,3,4,5,6,8,4};
        Set<Integer> set = new HashSet<>();
        for (int x:array) {
            //set用来去重
            // 给定一组数据 并且数据有重复的 去重
            set.add(x);
        }
        System.out.println(set);
    }
    public static void main3(String[] args) {
        Student student1 = new Student("1234");
        System.out.println(student1.hashCode());

        Student student2 = new Student("1234");
        System.out.println(student2.hashCode());

        HashBuck2<Student,String> hashBuck2 = new HashBuck2<>();
        hashBuck2.put(student1,"gaogao");
        String val = hashBuck2.get(student2);
        System.out.println(val);

    }
    public static void main2(String[] args) {
        Student student = new Student("1234");
        System.out.println(student.hashCode());

    }
    public static void main1(String[] args) {
        HashBuck hashBuck = new HashBuck();
        hashBuck.put(1,11);
        hashBuck.put(2,22);
        hashBuck.put(5,55);
        hashBuck.put(8,88);
        hashBuck.put(9,99);
        hashBuck.put(14,33);
        hashBuck.put(4,43);
        hashBuck.put(6,25);



        Integer val = hashBuck.get(51);
        System.out.println(val);
    }
}

        虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的, 也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是 O(1)  

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值