<<Java>> Hash(哈希表) 你会使用吗?知道底层原理吗?:三分钟一篇学会

简介: <<Java>> Hash(哈希表) 你会使用吗?知道底层原理吗?:三分钟一篇学会

什么是Hash(哈希表)?

① 先确定一个哈希函数:

hash (key) = key % capacity (通常会使用这种求余法,capacity是容量)


② 例子:

假如有一组数据集合:1,7,6,4,5,9 假设hash表容量为10

我们需要存入到哈希表中怎么存储?

0c57b983953e41d492814eba6f88efc3.png


我们将每个元素求到的hash数据存放到 hash表中得到


image.png


③ so?得出的结论


当我们查找元素的时候,只需要根据公式找到hashset的位置就可以直接找到元素的位置


,不必进行多次关键码的比较,搜索的速度比较快。


一般在查找一个元素时,要经过关键码的多次比较。


顺序查找的时间复杂度为 O(N)。平衡树查找的时间复杂度为树的高度,即O(logN)。


理想的搜索方法 : 不经过任何的比较,一次直接从表中得到搜索的元素。 很显然,顺序查找、平衡树查找都不是理性的,而哈希表是-------通过哈希函数是元素的存储位置与关键码之间能够建立起一一映射的关系,那么在查找的时通过该函数就可以直接找到该元素


哈希表插入/删除/查找的时间复杂度都是O(1)

冲突

① 概念(什么是冲突?)

不同的关键字通过 相同的哈希函数 计算出 相同的哈希地址,该现象称之为 哈希冲突 或 哈希碰撞。我们把 不同的关键码 而具有 相同哈希地址 的数据元素称为“ 同义词 ”。


② 冲突出现

  • 出现冲突的一个重要原因:哈希函数设计不够合理。
  • 常见哈希函数:(有很多很多,只列举俩)

        1.直接定制法  

(线性函数) Hash(key)= A*key + B

            使用 场 景 :需要事先知道关键字的分布情况,适合查找比较小且连续的情况

        2.除留余数法

   (上面的例子就是) Hash(key)= key%p (p接近地址数,但不大于地址数)


③ 冲突解决

  • 两种解决方法:闭散列开散列

闭散列(开放地址法)

  • 思想:如果哈希表没有被装满,则为把key存放到冲突位置中的下一个。


探测方法:

               1. 线性探测


                       直接插入到后面没有发生冲突的位置


                       缺陷:容易让产生冲突的元素堆积到一起


               2. 二次探测


                              第一次发生冲突,重新通过哈希函数计算H(i) = (H0+i*i)% m(m是地址数)


                               一直计算,直至计算得到的哈希地址不存在冲突。


开散列(哈希桶)

思想: 使用数组+链表+红黑树的方式,数组里面存放链表的头节点

图示:


image.png


开散列代码实现//重点


// key-value 模型
public class HashBucket {
    private static class Node {
        private int key;
        private int value;
        Node next;
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
    private Node[] array;
    private int size; // 当前的数据个数
    private static final double LOAD_FACTOR = 0.75;
    public int put(int key, int value) {
        int index = key % array.length;
// 在链表中查找 key 所在的结点
// 如果找到了,更新
// 所有结点都不是 key,插入一个新的结点
        for (Node cur = array[index]; cur != null; cur = cur.next) {
            if (key == cur.key) {
                int oldValue = cur.value;
                cur.value = value;
                return oldValue;
            }
        }
        Node node = new Node(key, value);
        node.next = array[index];
        array[index] = node;
        size++;
        if (loadFactor() >= LOAD_FACTOR) {    resize();    }
        return -1;
    }
    private void resize() {
        Node[] newArray = new Node[array.length * 2];
        for (int i = 0; i < array.length; i++) {
            Node next;
            for (Node cur = array[i]; cur != null; cur = next) {
                next = cur.next;
                int index = cur.key % newArray.length;
                cur.next = newArray[index];
                newArray[index] = cur;
            }
        }
        array = newArray;
    }
    private double loadFactor() {  return size * 1.0 / array.length;   }
    public HashBucket() {
        array = new Node[8];
        size = 0;
    }
    public int get(int key) {
        int index = key % array.length;
        Node head = array[index];
        for (Node cur = head; cur != null; cur = cur.next) {
            if (key == cur.key) {      return cur.value;    }
        }
        return -1;
    }
}


相关文章
|
22天前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
18天前
|
算法 Java
JAVA并发编程系列(8)CountDownLatch核心原理
面试中的编程题目“模拟拼团”,我们通过使用CountDownLatch来实现多线程条件下的拼团逻辑。此外,深入解析了CountDownLatch的核心原理及其内部实现机制,特别是`await()`方法的具体工作流程。通过详细分析源码与内部结构,帮助读者更好地理解并发编程的关键概念。
|
2天前
|
网络协议 安全 Java
Java Socket原理
Java Socket原理是指在Java中通过Socket实现的网络通信的基础理论与机制。Socket是网络中不同设备间通信的一种标准方式,它允许应用程序之间通过TCP/IP等协议进行数据交换。在Java中,利用Socket编程可以方便地创建客户端与服务器端应用,实现跨网络的数据传输功能,是互联网软件开发中的重要技术之一。它支持多种通信模式,如可靠的流式套接字(TCP)和数据报式套接字(UDP)。
|
17天前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
10天前
|
安全 Java 编译器
Java反射的原理
Java 反射是一种强大的特性,允许程序在运行时动态加载、查询和操作类及其成员。通过 `java.lang.reflect` 包中的类,可以获取类的信息并调用其方法。反射基于类加载器和 `Class` 对象,可通过类名、`getClass()` 或 `loadClass()` 获取 `Class` 对象。反射可用来获取构造函数、方法和字段,并动态创建实例、调用方法和访问字段。虽然提供灵活性,但反射会增加性能开销,应谨慎使用。常见应用场景包括框架开发、动态代理、注解处理和测试框架。
|
18天前
|
Java
Java的aop是如何实现的?原理是什么?
Java的aop是如何实现的?原理是什么?
18 4
|
22天前
|
存储 Java
JAVA并发编程AQS原理剖析
很多小朋友面试时候,面试官考察并发编程部分,都会被问:说一下AQS原理。面对并发编程基础和面试经验,专栏采用通俗简洁无废话无八股文方式,已陆续梳理分享了《一文看懂全部锁机制》、《JUC包之CAS原理》、《volatile核心原理》、《synchronized全能王的原理》,希望可以帮到大家巩固相关核心技术原理。今天我们聊聊AQS....
|
19天前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
16天前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
21天前
|
Java
JAVA并发编程ReentrantLock核心原理剖析
本文介绍了Java并发编程中ReentrantLock的重要性和优势,详细解析了其原理及源码实现。ReentrantLock作为一种可重入锁,弥补了synchronized的不足,如支持公平锁与非公平锁、响应中断等。文章通过源码分析,展示了ReentrantLock如何基于AQS实现公平锁和非公平锁,并解释了两者的具体实现过程。