CurrentHashMap巧妙利用位运算获取数组指定下标元素

先来了解一下数组对象在堆中的存储形式【数组长度,数组元素类型信息等】+ 【存放元素对象的空间】

Ma

基础信息实例数据内存填充
Mark Word,ClassPointer,数组长度第一个元素第二个元素固定的填充内容

所以我们想要获取某个下标的元素首先要获取这个元素的起始位置和每个元素长度

例如我们要取第二个元素的,首先知道第二个元素的起始位置,从其实位置开始按照每个元素的长度取指定的位数,就相当于获取了第二个元素的全部信息。

再来了解一下Java中一个特殊的类sun.misc.Unsafe

sun.misc.Unsafe是JDK提供的用于很底层编程的类,位于sun.misc包中。在有些底层编程的场景,Java语言层面办不到的事情,我们可能需要使用JNI,借助C语言去实现。但是使用JNI并不是唯一的选择,使用JNI会将代码绑定到特定的平台,使用Unsafe类可以保留Java语言代码对平台的独立性,又实现底层编程。

Unsafe类并没有public的构造函数,只提供了一个静态工厂方法,这个静态方法还只提供给JDK标准库自身的类调用,在我们自己随便建的一个普通的类调用这个静态工厂方法还会抛异常。

这个静态工厂方法的代码如下:

@CallerSensitive
public static Unsafe getUnsafe() {Class<?> caller = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");return theUnsafe;
}

可以使用反射的方式

import sun.misc.Unsafe;
import java.lang.reflect.Field;public class TestUnsafe {private static Unsafe unsafe;static {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);unsafe = (Unsafe) f.get(null);} catch (Exception e) {//}}}

 Unsafe功能列表

  • allocateMemory/freeMemory,分配、释放堆外内存DirectMemory(和c/cpp中的malloc一样)
  • CAS操作
  • copyMemory
  • defineClass(without security checks)
  • get/put address 使用堆外内存地址进行数据的读写操作
  • get/put volatile 使用堆外内存地址进行数据的读写操作 - volatile版本
  • loadFence/storeFence/fullFence 禁止指令重排序
  • park/unpark 阻塞/解除阻塞线程

Unsafe的数组操作

unsafe中,有两个关于数组的方法:

public native int arrayBaseOffset(Class<?> arrayClass);
public native int arrayIndexScale(Class<?> arrayClass);

arrayBaseOffset 数组第一个元素相对于数组对象起始地址的偏移量

arrayIndexScale就是指数组中每个元素所占用的空间大小,比如int[] scale就是4,long[] scale就是8,object[] scale就是4(指针大小)

// Unsafe mechanicsprivate static final sun.misc.Unsafe U;private static final long SIZECTL;private static final long TRANSFERINDEX;private static final long BASECOUNT;private static final long CELLSBUSY;private static final long CELLVALUE;private static final long ABASE;private static final int ASHIFT;static {try {U = sun.misc.Unsafe.getUnsafe();Class<?> k = ConcurrentHashMap.class;SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl"));TRANSFERINDEX = U.objectFieldOffset(k.getDeclaredField("transferIndex"));BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount"));CELLSBUSY = U.objectFieldOffset(k.getDeclaredField("cellsBusy"));Class<?> ck = CounterCell.class;CELLVALUE = U.objectFieldOffset(ck.getDeclaredField("value"));Class<?> ak = Node[].class;ABASE = U.arrayBaseOffset(ak);int scale = U.arrayIndexScale(ak);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);} catch (Exception e) {throw new Error(e);}}

这是CurrentHashMap中的源码我们需要注意的地方有:

Class<?> ak = Node[].class; 
ABASE = U.arrayBaseOffset(ak);
ABASE:数组第一个元素相对于数组对象起始地址的偏移量
int scale = U.arrayIndexScale(ak);数组中每个元素所占用的空间大小,返回的是所占空间的字节数
scale为2的n次方,2的n次方二进制表示就是某个位置为1其余位置为0
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
Integer.numberOfLeadingZeros(scale) 计算int型参数二进制表示数值最高位前面有几个0
由于int一共有32位,出去位1的一位剩下31位,31 - Integer.numberOfLeadingZeros(scale)就表示scale二进制表示最低位有几个连续的0
                                                                        32
                                                                        32位
000000000000000000000000000(27位)100000(5位)
Integer.numberOfLeadingZeros()31 - Integer.numberOfLeadingZeros()

    @Testpublic void printObjectArrayScale() throws NoSuchFieldException, IllegalAccessException {Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");theUnsafeInstance.setAccessible(true);Unsafe U = (Unsafe) theUnsafeInstance.get(Unsafe.class);Class<?> objectArr = Object[].class;Class<?> intArr = int[].class;Class<?> longArr = long[].class;Class<?> byteArr = byte[].class;int objectScale = U.arrayIndexScale(objectArr);int intScale = U.arrayIndexScale(intArr);int longScale = U.arrayIndexScale(longArr);int byteScale = U.arrayIndexScale(byteArr);System.out.println("Object[]的sacle=" + objectScale);System.out.println("int[]的sacle=" + intScale);System.out.println("long[]的sacle=" + longScale);System.out.println("byte[]的sacle=" + byteScale);
}

可以通过上面的方法测试,引用类型的数组的scale是4,因为虽然不同对象占用内存大小不一,但是对象数组每个元素是指向对应对象的引用,即内存地址

常规的想法是通过 scale * index 来计算某个元素相对于起始位置的偏移量

    @Testpublic void findArrayByBaseAndScale() throws NoSuchFieldException, IllegalAccessException {Class<Unsafe> clazz = Unsafe.class;Field unsafeField = clazz.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe)unsafeField.get(null);int[] temp = {1, 2, 3 , 4};int base = unsafe.arrayBaseOffset(int[].class);int scale = unsafe.arrayIndexScale(int[].class);System.out.println(unsafe.getInt(temp , base + 0 * scale));System.out.println(unsafe.getInt(temp , base + 1 * scale));System.out.println(unsafe.getInt(temp , base + 2 * scale));System.out.println(unsafe.getInt(temp , base + 3 * scale));
}

 可以看到顺利取到了数组元素

这里看看CurrentHashMap是怎么计算元素的偏移量的

    @SuppressWarnings("unchecked")static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);}

这里测试一下它的这种方法

    @Testpublic void findArrayByBaseAndScale1() throws NoSuchFieldException, IllegalAccessException {Class<Unsafe> clazz = Unsafe.class;Field unsafeField = clazz.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe)unsafeField.get(null);int[] temp = {1, 2, 3 , 4};int base = unsafe.arrayBaseOffset(int[].class);int scale = unsafe.arrayIndexScale(int[].class);int ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);System.out.println("第一个元素:" + unsafe.getInt(temp , base + (0 << ASHIFT) ));System.out.println("第二个元素:" + unsafe.getInt(temp , base + (1 << ASHIFT) ));System.out.println("第三个元素:" + unsafe.getInt(temp , base + (2 << ASHIFT) ));System.out.println("第四个元素:" + unsafe.getInt(temp , base + (3 << ASHIFT) ));}

 可以看到也顺利取到了数组的元素

熟悉位运算的很容易理解,其实这就是利用了一个位运算的技巧,通过向左移来实现扩大为2的n次方倍,而scale正好是2的n次方,所以可以这样计算

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/42623.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sorted Set 类型命令(命令语法、操作演示、命令返回值、时间复杂度、注意事项)

Sorted Set 类型 文章目录 Sorted Set 类型zadd 命令zrange 命令zcard 命令zcount 命令zrevrange 命令zrangebyscore 命令zpopmax 命令bzpopmax 命令zpopmin 命令bzpopmin 命令zrank 命令zscore 命令zrem 命令zremrangebyrank 命令zremrangebyscore 命令zincrby 命令zinterstor…

线程池案例

秒杀 需求 10个礼物20个客户抢随机10个客户获取礼物&#xff0c;另外10无法获取礼物 任务类 记得给共享资源加锁 public class MyTask implements Runnable{// 礼物列表private ArrayList<String> gifts ;// 用户名private String username;public MyTask( String user…

android Dialog全屏沉浸式状态栏实现

在Android中&#xff0c;创建沉浸式状态栏通常意味着让状态栏背景与应用的主题颜色一致&#xff0c;并且让对话框在状态栏下面显示&#xff0c;而不是浮动。为了实现这一点&#xff0c;你可以使用以下代码片段&#xff1a; 1、实际效果图&#xff1a; 2、代码实现&#xff1a;…

揭秘GPT-4o:未来智能的曙光

引言 近年来&#xff0c;人工智能&#xff08;AI&#xff09;的发展突飞猛进&#xff0c;尤其是自然语言处理&#xff08;NLP&#xff09;领域的进步&#xff0c;更是引人注目。在这一背景下&#xff0c;OpenAI发布的GPT系列模型成为了焦点。本文将详细探讨最新的模型GPT-4o&a…

Unity海面效果——6、反射和高光

Unity引擎制作海面效果 大家好&#xff0c;我是阿赵。 上一篇的结束时&#xff0c;海面效果已经做成这样了&#xff1a; 这个Shader的复杂程度已经比较高了&#xff1a; 不过还有一些美中不足的地方。 1、 海平面没有反射到天空球 2、 在近岸边看得到水底的部分&#xff0c;水…

一些关于C++的基础知识

引言&#xff1a;C兼容C的大部分内容&#xff0c;但其中仍有许多小细节的东西需要大家注意 一.C的第一个程序 #include <iostream> using namespace std;int main() {cout << "hello world!" << endl;return 0; } 第一次看这个是否感觉一头雾水…

数据挖掘——matplotlib

matplotlib概述 Mat指的是Matlab&#xff0c;plot指的是画图&#xff0c;lib即library&#xff0c;顾名思义&#xff0c;matplotlib是python专门用于开发2D图表的第三方库&#xff0c;使用之前需要下载该库&#xff0c;使用pip命令即可下载。 pip install matplotlib1、matpl…

elasticsearch SQL:在Elasticsearch中启用和使用SQL功能

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

服务注册Eureka

目录 一、背景 1、概念 2、CAP 理论 3、常见的注册中心 二、Eureka 三、搭建 Eureka Server 1、搭建注册中心 四、服务注册 五、服务发现 六、Eureka 和 Zooper 的区别 一、背景 1、概念 远程调用就类似于一种通信 例如&#xff1a;当游客与景区之间进行通信&…

Xubuntu24.04之设置高性能模式两种方式(二百六十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

苍穹外卖--新增员工

代码开发 package com.sky.controller.admin;import com.sky.constant.JwtClaimsConstant; import com.sky.dto.EmployeeDTO; import com.sky.dto.EmployeeLoginDTO; import com.sky.entity.Employee; import com.sky.properties.JwtProperties; import com.sky.result.Result…

Springboot各个版本维护时间

Springboot各个版本维护时间

MQTT教程--服务器使用EMQX和客户端使用MQTTX

什么是MQTT MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级、基于发布-订阅模式的消息传输协议&#xff0c;适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎&#xff0c;能够实现传感器、执行器和其它设备…

jmeter持续学习之----性能初级一些概念和指标

服务端为什么要进行性能测试 大量用户下&#xff0c;系统能否稳定运行&#xff08;比较多&#xff09; 用于硬件服务器的选型 用于软件技术的选型 性能测试关注的点 用户角度:响应时间 资源占用:并发用户数,TPS,资源占用(cpu,内存,JVM) 性能测试策略 基准测试:单用户测试,对…

去了字节跳动,才知道年薪40W的测试有这么多?

最近脉脉职言区有一条讨论火了&#xff1a; 哪家互联网公司薪资最‘厉害’&#xff1f; 下面的评论多为字节跳动&#xff0c;还炸出了很多年薪40W的测试工程师 我只想问一句&#xff0c;现在的测试都这么有钱了吗&#xff1f; 前几天还有朋友说&#xff0c;从腾讯跳槽去了字节&…

8.8.8.8 IP地址的作用

在跟着韦东山老师的学习手册中看见了关于8.8.8.8 IP用于检测网络状态&#xff0c;然后搜索了关于此IP的相关作用如下&#xff1a; 公共DNS服务&#xff1a;8.8.8.8是Google提供的两个公共DNS服务器地址之一&#xff08;另一个是8.8.4.4&#xff09;。DNS&#xff08;域名系统&a…

强化基石,引领未来:完善配套设施与提升服务水平

完善配套设施与提升服务水平对于产业园运营具有重要意义。它们不仅能够提升园区的硬件环境和整体形象&#xff0c;增强园区的吸引力和竞争力&#xff1b;还能够优化营商环境&#xff0c;降低企业运营成本&#xff0c;提高运营效率&#xff1b;同时推动园区创新&#xff0c;形成…

基于Java技术的网吧管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术&#xff0c;B/S结构 工具&#xff1a;MyEclipse&#xff0c;MySQL 系统展示 首页 个人中…

【技术追踪】DiffuMatting:使用抠图级别注释合成任意对象(ECCV-2024)

万物生&#xff1a;Diffusion与绿幕抠图&#xff0c;影视领域的福音~ 论文&#xff1a;DiffuMatting: Synthesizing Arbitrary Objects with Matting-level Annotation 代码&#xff1a;https://github.com/HUuxiaobin/DiffuMatting &#xff08;即将开源&#xff09; 0、摘要 …

(补充):java各种进制和文本、图像、音频在计算机中的存储方式

文章目录 前言一、进制1 逢几进一2 常见进制在java中的表示3 进制中的转换(1)任意进制转十进制(2)十进制转其他进制二、计算机中的存储1 计算机的存储规则(文本数据)(1)ASCII码表(2)编码规则的发展演化2 计算机的存储规则(图片数据)(1)分辨率、像素(2)黑白图与灰度…