Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >斐波那契散列算法和hashMap实践

斐波那契散列算法和hashMap实践

原创
作者头像
xbhog
发布于 2023-01-22 13:31:10
发布于 2023-01-22 13:31:10
1.1K1
举报
文章被收录于专栏:开发技能乱炖开发技能乱炖

斐波那契散列和hashMap实践

适合的场景:抽奖(游戏、轮盘、活动促销等等)

如果有不对的地方,欢迎指正!

HashMap实现数据散列:

配置项目,引入pom.xml:

代码语言:html
AI代码解释
复制
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.5</version>
</dependency>

前置条件:

  1. 存放数组:128位
  2. 100个数据进行散列
  3. 若有hash冲突,使用拉链法

首先,初始化100个随机数,这里采用雪花算法snowFlake,采用灵活注解引用,声明为Component,

简单了解下SnowFlake工具类实现方式:

代码语言:java
AI代码解释
复制
import com.example.containstest.containsTestDemo.mapper.FileNameAndType;
import com.example.containstest.containsTestDemo.mapper.FileNameInsertMapper;
import com.example.containstest.containsTestDemo.pojo.DatagenertionDao;
import com.example.containstest.containsTestDemo.pojo.FileNameType;
import com.example.containstest.containsTestDemo.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class SnowFlake implements IIdGenerator {

    private Snowflake snowflake;

    @PostConstruct
    public void init(){
        // 0 ~ 31 位,可以采用配置的方式使用
        long workerId;
        try {
            workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
        }catch (Exception e){
            workerId = NetUtil.getLocalhostStr().hashCode();
        }
        workerId = workerId >> 16 & 31;
        long dataCenterId = 1L;
        snowflake = IdUtil.createSnowflake(workerId,dataCenterId);
    }


    @Override
    public long nextId() {
        return snowflake.nextId();
    }
}

循环100,取其随机数保存列表中:

代码语言:java
AI代码解释
复制
List<String> list = new ArrayList<>();
//保存idx和重复的值
Map<Integer, String> map = new HashMap<>();
for(int i = 0; i < 101; i++){
    list.add(String.valueOf(snowFlake.nextId()));
}

创建数据散列到的数组大小,这里取128

代码语言:java
AI代码解释
复制
//定义要存放的数组 模拟初始化为128
String[] res = new String[128];

遍历保存的数组,计算出当前数值的hash值,然后到数组对应的下标处对应;

  1. 为空。当前key赋值到该数组下标值
  2. 不为空,表示hash冲突,这里采用字符串拼接模拟碰撞后使用的拉链法
  3. map存储对应idxkey
  4. 对重复的散列的值进行排序输出
代码语言:java
AI代码解释
复制
for(String key : list){
    //计算hash值,未使用扰动函数
    int idx = key.hashCode() & (res.length - 1);
    log.info("key的值{},idx的值{}",key,idx);
    if(null == res[idx]){
        res[idx] = key;
        continue;
    }
    res[idx] = res[idx] +"->" + key;
    map.put(idx,res[idx]);
}
//排序
mapSort(map);

map排序:

代码语言:java
AI代码解释
复制
private void mapSort(Map<Integer, String> map) {
    // 按照Map的键进行排序
    Map<Integer, String> sortedMap = map.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(
                    Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            (oldVal, newVal) -> oldVal,
                            LinkedHashMap::new
                    )
            );
    log.info("====>HashMap散列算法碰撞数据:{}",JSON.toJSONString(sortedMap));
}

未使用扰动函数HashMap散列输出结果展示:

代码语言:json
AI代码解释
复制
{
    28: "1596415617815183397->1596415617815183430",
    29: "1596415617815183398->1596415617815183431",
    30: "1596415617815183399->1596415617815183432",
    59: "1596415617815183363->1596415617815183440",
    60: "1596415617815183364->1596415617815183441",
    61: "1596415617815183365->1596415617815183442",
    62: "1596415617815183366->1596415617815183443",
    63: "1596415617815183367->1596415617815183400->1596415617815183444",
    64: "1596415617815183368->1596415617815183401->1596415617815183445",
    65: "1596415617815183369->1596415617815183402->1596415617815183446",
    66: "1596415617815183403->1596415617815183447",
    67: "1596415617815183404->1596415617815183448",
    68: "1596415617815183405->1596415617815183449",
    90: "1596415617815183373->1596415617815183450",
    91: "1596415617815183374->1596415617815183451",
    92: "1596415617815183375->1596415617815183452",
    93: "1596415617815183376->1596415617815183453",
    94: "1596415617815183377->1596415617815183410->1596415617815183454",
    95: "1596415617815183378->1596415617815183411->1596415617815183455",
    96: "1596415617815183379->1596415617815183412->1596415617815183456",
    97: "1596415617815183413->1596415617815183457",
    98: "1596415617815183414->1596415617815183458",
    99: "1596415617815183415->1596415617815183459",
    121: "1596415617815183383->1596415617815183460",
    125: "1596415617815183387->1596415617815183420",
    126: "1596415617815183388->1596415617815183421",
    127: "1596415617815183389->1596415617815183422"
}

针对上述代码,修改int idx = key.hashCode() & (res.length - 1);为下面:

代码语言:java
AI代码解释
复制
int idx =  (res.length - 1) & (key.hashCode() ^ (key.hashCode() >>> 16));

使用扰动函数HashMap散列输出结果展示:

代码语言:json
AI代码解释
复制
{
    44: "1596518378456121344->1596518378456121388",
    67: "1596518378460315650->1596518378460315694",
    72: "1596518378456121351->1596518378456121395",
    73: "1596518378456121350->1596518378456121394",
    83: "1596518378456121345->1596518378456121389",
    92: "1596518378460315651->1596518378460315695",
    93: "1596518378460315652->1596518378460315696"
}

从对比结果可以看到,加入扰动函数后hash碰撞减少了很多。

斐波那契散列算法

前置条件:

  1. 生成模拟数据:随机且不重复的100个数
  2. 声明散列数组:大小128
  3. 若有hash冲突,保存map,方便数据查看

静态变量声明:

代码语言:java
AI代码解释
复制
//黄金分割点
private static final int HASH_INCREMENT = 0x61c88647;
private static int range = 100;

按照惯例,初始化数组,模拟数据;

代码语言:java
AI代码解释
复制
List<Integer> listThreadLocal = new ArrayList<>();

Map<Integer, String> map = new HashMap<>();
//定义要存放的数组 模拟初始化为128
Integer[] result = new Integer[128];
result = getNumber(range);
//......

//-----方法
/**
 * 随机生成100以内不重复的数
 * @param total
 * @return
 */
public static Integer[] getNumber(int total){
    Integer[] NumberBox = new Integer[total];			//容器A
    Integer[] rtnNumber = new Integer[total];			//容器B

    for (int i = 0; i < total; i++){
        NumberBox[i] = i;		//先把N个数放入容器A中
    }
    Integer end = total - 1;

    for (int j = 0; j < total; j++){
        int num = new Random().nextInt(end + 1);	//取随机数
        rtnNumber[j] = NumberBox[num];			//把随机数放入容器B
        NumberBox[num] = NumberBox[end];			//把容器A中最后一个数覆盖所取的随机数
        end--;									//缩小随机数所取范围
    }
    return rtnNumber;							//返回int型数组
}

遍历模拟的数据,通过源码阅读,可以找到new ThreadLocal<String>().set("xbhog");

注意点,threadLocal实现主要是在ThreadLoacalMap中

img
img
代码语言:java
AI代码解释
复制
//2
private final int threadLocalHashCode = nextHashCode();
//4  默认值0
private static AtomicInteger nextHashCode = new AtomicInteger();
//3步骤使用
private static final int HASH_INCREMENT = 0x61c88647;

//3
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

//key和len是外部传入  1
int i = key.threadLocalHashCode & (len-1);

可以看到每次计算哈希值的时候,都会加一次HASH_INCREMENT黄金分割点,来更好的散列数据,然后模拟该操作:代码如下

代码语言:java
AI代码解释
复制
for(int i = 0; i < listThreadLocal.size(); i++){
    hashCode = listThreadLocal.get(i) * HASH_INCREMENT + HASH_INCREMENT;
    Integer idx = (hashCode & 127);
    log.info("key的值{},idx的值{}",listThreadLocal.get(i),idx);
    if(null == result[idx]){
        result[idx] = listThreadLocal.get(i);
        continue;
    }
    String idxInRes = map.get(idx);
    String idxInRess = idxInRes + "->" + listThreadLocal.get(i);
    map.put(idx,idxInRess);
}

进行冲突后重复值排序

代码语言:java
AI代码解释
复制
//map排序
if(CollectionUtil.isEmpty(map)){
    log.info("斐波那契额散列数据集:{}",JSON.toJSONString(result));
    System.out.println("===》无重复数据,不需要排序");
    return;
}
mapSort(map);

使用斐波那契散列算法输出结果展示:

代码语言:text
AI代码解释
复制
斐波那契额散列数据集:38,15,29,22,55,86,70,64,47,32,67,7,60,85,97,95,58,46,14,83,12,72,18,96,36,20,76,59,6,33,50,30,23,42,81,31,66,71,82,61,53,84,41,45,74,63,89,77,90,16,8,37,1,62,65,99,51,78,91,39,5,57,27,56,44,13,92,25,0,24,80,3,94,26,40,34,73,35,88,2,87,11,93,54,69,68,10,17,43,48,19,9,79,21,98,52,4,28,75,49]
===》无重复数据,不需要排序

由上我们可以看到,没有重复的数据,全部比较完美的散列到不同的地方。

参考文章:

https://blog.csdn.net/qq_26327971/article/details/104757316

https://juejin.cn/post/6844903985808146439

https://juejin.cn/user/3913917126415166

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
1 条评论
热度
最新
HASH_INCREMENT 算的对吗 Long.toHexString((long) ((2L << 31) * ((Math.sqrt(5) - 1) / 2))) -> 9e3779b9
HASH_INCREMENT 算的对吗 Long.toHexString((long) ((2L &lt;&lt; 31) * ((Math.sqrt(5) - 1) / 2))) -&gt; 9e3779b9
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
高效编程之hashmap你必须要懂的知识点
以前看Java的招聘要求:Java基础扎实,熟悉常用集合类,多线程,IO,网络编程,经常会疑惑,集合类不就ArrayList,HashMap会用,熟悉下API不就好了么,知道得越多才会发觉不知道的还有好多!     一入Java深似海啊
矿泉水
2018/05/11
1.1K1
高效编程之hashmap你必须要懂的知识点
HashMap深度学习,扰动函数、负载因子,原理加实践,让懂了就是真的懂!
得益于Doug Lea老爷子的操刀,让HashMap成为使用和面试最频繁的API,没办法设计的太优秀了!
小傅哥
2020/08/10
1.6K0
HashMap深度学习,扰动函数、负载因子,原理加实践,让懂了就是真的懂!
【Java 并发】详解 ThreadLocal
ThreadLocal 主要用来提供线程局部变量,也就是变量只对当前线程可见,本文主要记录一下对于 ThreadLocal 的理解。更多关于 Java 多线程的文章可以转到 这里。
零式的天空
2022/03/23
5590
《Java 数据结构与算法》第5章:哈希表(散列)
哈希散列的想法在不同的地方独立出现。1953 年 1 月,汉斯·彼得·卢恩 ( Hans Peter Luhn ) 编写了一份IBM内部备忘录,其中使用了散列和链接。开放寻址后来由 AD Linh 在 Luhn 的论文上提出。大约在同一时间,IBM Research的Gene Amdahl、Elaine M. McGraw、Nathaniel Rochester和Arthur Samuel为IBM 701汇编器实现了散列。 线性探测的开放寻址归功于 Amdahl,尽管Ershov独立地有相同的想法。“开放寻址”一词是由W. Wesley Peterson在他的文章中创造的,该文章讨论了大文件中的搜索问题。
小傅哥
2022/12/13
7420
《Java 数据结构与算法》第5章:哈希表(散列)
美团面试:说一下 ThreadLocal 的原理?网友:现在面试不看源码不行
上周我侥幸通过美团一面,岗位是java后端开发工程师。美团面试官给我进行了二面。面试过程中他问了ThreadLocal原理(上次问线程池,这次问ThreadLocal,美团爸爸这么喜欢线程安全机制么),今天详细讲一讲ThreadLocal原理。
终码一生
2022/04/14
2340
美团面试:说一下 ThreadLocal 的原理?网友:现在面试不看源码不行
《程序员数学:斐波那契》—— 为什么不能用斐波那契散列,做数据库路由算法?
斐波那契数列出现在印度数学中,与梵文韵律有关。在梵语诗歌传统中,人们对列举所有持续时间为 2 单位的长 (L) 音节与 1 单位持续时间的短 (S) 音节并列的模式很感兴趣。用给定的总持续时间计算连续 L 和 S 的不同模式会产生斐波那契数:持续时间m单位的模式数量是F(m + 1)。
小傅哥
2022/12/13
1K0
《程序员数学:斐波那契》—— 为什么不能用斐波那契散列,做数据库路由算法?
ThreadLocal详解
保证线程安全一是可以同步对共享资源的操作和访问,二是不共享。就像ThreadLocal这样,给每个线程分一个对象,每个线程也只能访问到自己的这个对象,从而保证线程安全。比如SimpleDateFormat这个类,咋也没想到它是线程不安全的,既然线程不安全我们就给每一个线程都实例化一个SimpleDateFormat,自己用自己的就安全了,ThreadLocal就给我们实现了分配线程私有对象这么个功能。
naget
2019/07/03
4280
ThreadLocal详解
散列表采用线性探测法会出现_平方探测法解决冲突
第一、前言 ThreadLocal使用的是自定义的ThreadLocalMap,接下来我们来探究一下ThreadLocalMap的hash冲突解决方式。 第二、ThreadLocal的set()方法
全栈程序员站长
2022/11/01
3740
散列表采用线性探测法会出现_平方探测法解决冲突
面试28k职位,老乡面试官从HashCode到HashMap给我讲了一下午!
☺️可能有点标题夸张,但本文通篇干货,要不亲身实践各项知识点,很难有这样的深度的总结。有时候我们会抱怨找工作难,但同样企业招聘也难,面试官向我透漏,为了招聘3个高开,以及筛选了200份简历,面试了70场。
小傅哥
2020/08/23
9210
面试28k职位,老乡面试官从HashCode到HashMap给我讲了一下午!
美团面试问ThreadLocal,学妹一口气给他说了四种!
ThreadLocal类顾名思义可以理解为线程本地变量。也就是说如果定义了一个ThreadLocal, 每个线程往这个ThreadLocal中读写是线程隔离,互相之间不会影响的。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。
java金融
2020/09/12
7040
聊聊java中的哪些Map:(十)各种map的总结
前面已经对常用的各种map进行了介绍,现在将这些遇到的map放在一起进行对比,这样便于学习和记忆。
冬天里的懒猫
2020/09/07
1.5K0
聊聊java中的哪些Map:(十)各种map的总结
HashMap原理解析
1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。       数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难; 链表 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。 哈希表 那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们
xiangzhihong
2018/02/01
6420
HashMap原理解析
面试被问到HashMap 底层原理?看完这边文章绝对不慌!
存储:put 方法 put(key,value) 查询 : get 方法 get(key) java 代码如下
全栈程序员站长
2022/08/31
2880
HashMap底层原理
   HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
翎野君
2023/05/12
3150
HashMap底层原理
JDK8;HashMap:再散列解决hash冲突 ,源码分析和分析思路
首先,有一个问题:假如我们现在有一个容量为16的数组,现在我想往里面放对象,我有15个对象。
执生
2020/09/27
9280
并发编程系列之ThreadLocal实现原理
ThreadLocal看词义,线程本地变量?线程的变量,要怎么定义?怎么使用?ThreadLocal是线程安全的?下面给出一个简单例子,引出本文
SmileNicky
2022/05/07
2480
并发编程系列之ThreadLocal实现原理
一文搞懂 ThreadLocal 原理
当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了。
武培轩
2020/04/08
5570
深入分析——HashSet是否真的无序?(JDK8)
《Core Java Volume I—Fundamentals》中对HashSet的描述是这样的:
BWH_Steven
2019/08/09
1.2K0
HashMap 的实现原理
> 1. hashcode 是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
子晋
2022/01/18
2980
ThreadLocal的使用及原理分析
ThreadLocal称作线程本地存储。简单来说,就是ThreadLocal为共享变量在每个线程中都创建一个副本,每个线程可以访问自己内部的副本变量。这样做的好处是可以保证共享变量在多线程环境下访问的线程安全性。
日薪月亿
2019/05/14
5680
ThreadLocal的使用及原理分析
推荐阅读
相关推荐
高效编程之hashmap你必须要懂的知识点
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档