Java集合框架|步步解析HashMap底层源码

java集合之Map篇——HashMap(底层源码非常详细说明) 前面先做了红黑树的讲解,就是为了为了Map集合做铺垫,Map的几个实现集合底层都用到了红黑树。由于HashMap的东西有点多,HashTable和TreeMap下篇再说明。 阅读详情

本章主要详细分析HashMap底层源码

HashMap数据结构(JDK1.8)

  • 数组+链表+红黑树

在这里插入图片描述

HashMap的层次关系与继承结构

  • 继承了一个AbstractMap,也就是用来减轻实现Map接口的编写负担。

hashconstruct

HashMap的实现接口

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
  ...
}
  • Map<K,V>:在AbstractMap抽象类中已经实现过的接口。

  • Cloneable:能够使用Clone()方法,在HashMap中,实现的是浅拷贝,即对拷贝对象的改变会影响被拷贝的对象。

  • Serializable:能够使之序列化,即可以将HashMap对象保存至本地,之后可以恢复状态。

HashMap的属性

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,
Cloneable, Serializable {
    // 序列号
    private static final long serialVersionUID = 362498820763181265L; 
  
  	/*
  		 初始容量:哈希表中桶的数量 (桶这个概念怎么理解?可以理解为相当于在数组中每个位置上放一个桶装元素)
  		 每个桶里就是装一个链表,链表中可以有很多个元素,这就是桶的意思。也就相当于把元素都放在桶中
  	*/
    // 默认的初始容量是16 
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
  
    // 最大容量 也就是最大的容量为2^30
    static final int MAXIMUM_CAPACITY = 1 << 30;
  
    /*
    	 加载因子:哈希表在其容量自动增加之前可以达到多满的一种尺度
    	 loadFactor的默认值为0.75f,计算HashMap的实时装载因子的方法为:size/capacity,size指存放元素的个数
    	 
    	 为什么加载因子为0.75
    	 加载因子过高,例如为1,虽然减少了空间开销,提高了空间利用率,但同时也增加了查询时间成本
    	 加载因子过低,例如0.5,虽然可以减少查询时间成本,但是空间利用率很低,同时提高了rehash(即重建内部数据结构)操作的次数
    	 选择0.75作为默认的加载因子是提高空间利用率和减少查询成本的折中,hash桶中遵循泊松分布,选择0.75的话hash碰撞最小
    */
    // 默认的加载因子为0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
  
    // 当桶(bucket)上的结点数大于这个值时会转成红黑树
    static final int TREEIFY_THRESHOLD = 8;
  
    // 当桶(bucket)上的结点数小于这个值时树转链表
    static final int UNTREEIFY_THRESHOLD = 6;
  
  	/*
  	   桶中结构转化成红黑树的两个条件:1.桶中链表长度要超过8,注意这里是超过8,可看下面源码 2.数组长度要达到64
  		 结合之前的源码,数组的某个桶有一个node,并且key和要添加的node不同,才会触发下列语句
  		 也就是说,当binCount=0,要往该桶put第2个node;当binCount=1,要往该桶put第3个node
  		 以此类推,当binCount=7,要往该桶put第9个node,而该桶已经有8个node了
       for (int binCount = 0; ; ++binCount) {
           if ((e = p.next) == null) {
             p.next = newNode(hash, key, value, null);
             if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
               treeifyBin(tab, hash);
             break;
           }
           if (e.hash == hash &&
               ((k = e.key) == key || (key != null && key.equals(k))))
             break;
           p = e;
        }
			 final void treeifyBin(Node<K,V>[] tab, int hash) {
        ...
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        ...
       }
  	*/
    // 桶中结构转化为红黑树对应的table的最小大小
    static final int MIN_TREEIFY_CAPACITY = 64;
  
    // 存储元素的数组,总是2的幂次倍
    transient Node<k,v>[] table;
  
    // 存放具体元素的集
    transient Set<map.entry<k,v>> entrySet;
  
    // 存放元素的个数,注意这个不等于数组的长度。
    transient int size;
  
    // 每次扩容和更改map结构的计数器
    transient int modCount;
  
    // 临界值 threshold = capacity * loadFactor 当size>=threshold的时候,那么就要考虑对数组的扩增
    int threshold;
  
    // 加载因子
    final float loadFactor;
}

threshold

HashMap的构造方法

无参构造 HashMap()

public HashMap() {
    //加载因子默认0.75
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

有参构造 HashMap(int initialCapacity)

public HashMap(int initialCapacity) {
    //调用HashMap(int initialCapacity, float loadFactor) 自定义初始容量
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

有参构造 HashMap(int initialCapacity, float loadFactor)

//自定义初始容量和加载因子
public HashMap(int initialCapacity, float loadFactor) {
    //初始容量不能小于0,否则报错
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //初始容量不能大于最大值,否则为最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //加载因子不能小于或等于0,不能为非数字
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    //初始化加载因子
    this.loadFactor = loadFactor;
    //初始化threshold大小
    this.threshold = tableSizeFor(initialCapacity);
}

//源码的解释是:返回一个指定容量大小的2次幂,
//其实就是返回比给定参数大的、最接近的2的n次方那个数,比如传入10返回16,传入18返回32
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//>>>为逻辑右移 |为或,也就是00得0,11得1,10得0,01得1
比如说tableSizeFor(10) 以8位为例,n=cap-1=9 
n=00001001 n >>> 1,n=00000100  n |= n >>> 1,n=00001101=13
n=00001101 n >>> 2,n=00000011  n |= n >>> 2,n=00001111=15
n=00001111 n >>> 4,n=00000000  n |= n >>> 4,n=00001111=15
之后几轮都是如此最后执行完 n |= n >>> 16之后n=15
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
n不大于MAXIMUM_CAPACITY所以执行n+1=16
n大于0所以返回16

有参构造 HashMap(Map<? extends K, ? extends V> m)

public HashMap(Map<? extends K, ? extends V> m) {
    //初始化加载因子
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    //将m中的所有元素添加至HashMap中
    putMapEntries(m, false);
}

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    //size()方法返回哈希映射中存在的键/值映射的数量
    int s = m.size();
    if (s > 0) {
      //table为空
      if (table == null) { // pre-size
        //+1.0F是因为((float)s / loadFactor)使用float计算,在转换成整数的时候会进行舍入,为了保证最终计算出来的size足够大不至于触发扩容,所以进行了+ 1.0F操作。
        //size/loadFactor=capacity 可以理解为ft存的是m的容量
        float ft = ((float)s / loadFactor) + 1.0F;
        //如果ft小于最大容量则t=ft,否则使t=最大容量
        int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                 (int)ft : MAXIMUM_CAPACITY);
        //如果t > threshold,即传进来的m的容量达到阈值,初始化threshold
        if (t > threshold)
          threshold = tableSizeFor(t);
      }
      //table不为空,如果s > threshold,即传进来的m的size达到阈值。扩容
      else if (s > threshold)
        //resize函数会在put方法中讲到
        resize();
      
      //将map中的元素逐一添加到HashMap中
      for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
        K key = e.getKey();
        V value = e.getValue();
        //putVal函数会在put方法中讲到
        putVal(hash(key), key, value, false, evict);
      }
    }
}

HashMap核心方法

put(K key, V value)

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
//hash函数是通过key来计算出hash值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//进入putVal函数
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
  	// table为空或者长度为0,进行扩容
    if ((tab = table) == null || (n = tab.length) == 0)
      //resize函数的分析在下面,返回之后将数组赋给tab,用n记录数组长度
      n = (tab = resize()).length;
  	//判断数组的hash位置是否为空
    if ((p = tab[i = (n - 1) & hash]) == null)
      //如果为空则直接插入newNode
      tab[i] = newNode(hash, key, value, null);
    //如果不为空
    else {
      Node<K,V> e; K k;
      //当前位置的节点和新插入的节点的key值相同
      if (p.hash == hash &&
          ((k = p.key) == key || (key != null && key.equals(k))))
        //则将e指向p
        e = p;
      //如果该节点为TreeNode类型
      else if (p instanceof TreeNode)
        //就按树的方式进行比对
        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      //进行到这一步,说明这个桶存的是单向链表
      else {
        //遍历链表
        for (int binCount = 0; ; ++binCount) {
          //e指向p的下一个,如果该位置为空,说明已经遍历到最后了
          if ((e = p.next) == null) {
            //然后往这个节点后面添加newNode
            p.next = newNode(hash, key, value, null);
            //如果链表的个数超过8,可能执行下列链表和树之间的转换
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
              treeifyBin(tab, hash);
            break;
          }
          //e指向p的下一个,该节点不为空,就比较当前位置的节点和新插入的节点的key值,如果相同,则跳出循环
          if (e.hash == hash &&
              ((k = e.key) == key || (key != null && key.equals(k))))
            break;
          p = e;
        }
      }
      //如果e不为空
      if (e != null) { // existing mapping for key
        //oldValue记录下e的value值
        V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
          //e的value为新传进来的value
          e.value = value;
        //访问后回调
        afterNodeAccess(e);
        return oldValue;
      }
    }
    ++modCount;
    //如果实际大小大于阈值则扩容
    if (++size > threshold)
      resize();
    //插入后回调
    afterNodeInsertion(evict);
    return null;
}

//resize函数
final Node<K,V>[] resize() {
    //当前所有元素所在的数组,称为老的元素数组
    Node<K,V>[] oldTab = table;
  	//oldCap记录下老的容量,如果老的元素数组 = null则oldCap = 0,否则就为老数组元素的长度
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    //oldThr记录下老的扩容阀值
    int oldThr = threshold;
    //创建新的容量newCap,创建新的扩容阈值newThr,赋值为0
    int newCap, newThr = 0;
    //如果oldCap > 0,说明已经存在元素
    if (oldCap > 0) {
      //如果老容量大于等于最大容量
      if (oldCap >= MAXIMUM_CAPACITY) {
        //扩容阀值设置为int最大值(2的31次方 -1) 
        threshold = Integer.MAX_VALUE;
        //返回老的数组
        return oldTab;
      }
      //如果老容量小于最大容量,大于等于默认初始容量,即数组元素个数在正常范围内,那么新的容量newCap为老的数组容量oldCap的2倍
      //<< 1为算术左移1位,左移1位相当于乘以2
      else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
               oldCap >= DEFAULT_INITIAL_CAPACITY)
        //新的扩容阈值newThr为老的扩容阈值oldThr的2倍
        newThr = oldThr << 1; // double threshold
    }
    //如果oldCap = 0,说明里面没有元素
    else if (oldThr > 0) // initial capacity was placed in threshold
      //如果老的扩容阀值oldThr大于0,那么设置新的容量newCap为该阀值
      newCap = oldThr;
  	//oldCap = 0并且oldThr = 0,使用缺省值(如使用HashMap()构造函数,之后再插入一个元素会调用resize函数,会进入这一步)
    else {               // zero initial threshold signifies using defaults
      //设置新的容量为16
      newCap = DEFAULT_INITIAL_CAPACITY;
      //设置新数组扩容阀值为 16*0.75 = 12
      newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    //说明是这个else if (oldThr > 0){}存在的情况
    if (newThr == 0) {
      //计算得到新的阈值
      float ft = (float)newCap * loadFactor;
      //将得到的阈值赋给newThr
      newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                (int)ft : Integer.MAX_VALUE);
    }
  	//经过以上这些情况之后,最终把newThr赋值给threshold,即设置扩容阀值为新的阀值
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
  	//创建新的数组(对于第一次添加元素,那么这个数组就是第一个数组;对于存在oldTab的时候,那么这个数组就是要需要扩容到的新数组)
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  	//将table指向新的数组newTab
    table = newTab;
  	//如果老的数组不为空,说明涉及到元素的移动
    if (oldTab != null) {
      //遍历老数组
      for (int j = 0; j < oldCap; ++j) {
        //创建一个Node结点
        Node<K,V> e;
        //如果当前位置元素不为空,那么需要设计到元素的移动
        if ((e = oldTab[j]) != null) {
          //释放掉老数组对于要转移走的元素的引用(主要为了使得数组可被回收)
          oldTab[j] = null;
          //如果元素没有下一个节点,说明该元素不存在hash冲突,则进行直接赋值
          if (e.next == null)
            //比如e的hash值为21没有进行扩容时容量为16,进行扩容之后的容量为32,以8位为例
            //扩容前,21->00010101 16-1=15->00001111 00010101&00001111=5
            //扩容后,21->00010101 32-1=31->00011111 00010101&00011111=21 增加了oldCap个长度
            //hash & (newCap - 1)等同于hash%newCap
            newTab[e.hash & (newCap - 1)] = e;
          //如果该节点为TreeNode类型
          else if (e instanceof TreeNode)
            //就在树中添加节点
            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
          //进行到这步则e.next != null,如果该元素有下一个节点,那么说明该位置上存着个链表
          else { // preserve order
            //创建低位首尾节点指针 低位指的是新数组的0到oldCap-1
            Node<K,V> loHead = null, loTail = null;
            //创建高位收尾节点指针 高位指定的是oldCap到newCap-1
            Node<K,V> hiHead = null, hiTail = null;
            //创建next节点
            Node<K,V> next;
            do {
              //让这个节点指针指向e的下一个节点
              next = e.next;
              //(e.hash & oldCap) == 0则说明该节点的位置不会发生变化
              //比如e.hash=10->00001010 oldCap=16->00010000 e.hash&oldCap=0则位置不发生变化
              if ((e.hash & oldCap) == 0) {
                //如果尾节点指针为空
                if (loTail == null)
                  //则e为低位链表的首节点,低位首节点指针指向e
                  loHead = e;
                //如果尾节点指针不为空
                else
                  //则e添加到低位链表的最后一个节点(尾节点)
                  loTail.next = e;
                //让低位尾节点指针指向e
                loTail = e;
              }
              //(e.hash & oldCap) == 1则说明该节点的位置发生变化
              //比如e.hash=17->00010001 oldCap=16->00010000 e.hash&oldCap=1则位置发生变化
              //新下标位置 = 原下标位置 + 老数组长度
              //else语句解释同上,只不过添加到高位链表中
              else {
                if (hiTail == null)
                  hiHead = e;
                else
                  hiTail.next = e;
                hiTail = e;
              }
            } while ((e = next) != null);//然后e指向next,也就是下一个节点,如果不为空,继续循环
            //如果低位链表尾节点指针不为空
            if (loTail != null) {
              //把低位链表尾节点的下一个赋为空
              loTail.next = null;
              //低位的元素组成的链表还是放置在原来的位置
              newTab[j] = loHead;
            }
            //如果高位链表尾节点指针不为空
            if (hiTail != null) {
              //把高位链表尾节点的下一个赋为空
              hiTail.next = null;
              //高位的元素组成的链表放置的位置只是在原有位置上偏移了老数组的长度个位置
              newTab[j + oldCap] = hiHead;
            }
          }
        }
      }
    }
    return newTab;
}

Hi, welcome to JasperのBlog!

Java集合、多线程、反射和Spring框架总结,源码解析 Java集合、多线程、反射和Spring框架总结,源码解析 一、集合 - 通过不同的数据结构存储以及操作数据的工具 1.1 Collection 1.1.1 ArrayList、Vector 1.1.1.1 底层原理 ArrayList和Vector底层都是由动态数组实现的 1.1.1.2 ArrayList VS Vector ArrayList是线程不安全的集合,而Vector是线程安全的集合。 Vector本质是JDK1.0的产物,但是集合体系是JDK1.2才推出的新特性。因此,JDK1.2之后 阅读详情

相关推荐

Java集合源码笔记:HashMap+List集合

大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。麻烦帮忙转发一下这篇文章+关注我《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。

2301_79835671的博客 810

Java集合(底层源码分析)

Java集合的整体结构描述,ArrayList以及HashMap源码解释,Java源码解释解析

分享技术和学习技术两不误 688

Java集合框架底层源码

集合底层源码 Collection List ArrayList public abstract class AbstractList<E>{ //操作数 protected transient int modCount = 0;//1 } ​ public class ArrayList<E> extends AbstractList<E> implements List<E>{ //空数组的实例(长度为0的数组实...

qq_51772112的博客 4210

Java集合 : 底层原理剖析

Java高级编程之集合,根据尚硅谷宋红康Java课程总结,适合新手学习,也可作为查阅文档

houjingchuan的博客 1787

Java 集合框架:HashMap 的介绍、使用、原理与源码解析

HashMap 根据是一个键值对集合,采用 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。HashMap 最多只允许一条记录的键为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。

栗筝i的博客 8726

多路输出电源的交叉调整率:从理论到无源优化实践

本文深入探讨了多路输出电源中的交叉调整率问题,分析了其物理根源及无源优化方法。通过漏感与耦合的理论分析,结合加权反馈、电感耦合等五种无源优化技术,帮助工程师有效解决交叉调整率挑战。文章还以TDK-Lambda CUT75系列为例,展示了优化技术的实际应用与效果。

weixin_26886855的博客 280

JDK8 HashMap源码 putMapEntries解析

putMapEntries函数会被HashMap的拷贝构造函数public HashMap(Map<? extends K, ? extends V> m)或者Map接口的putAll函数(被HashMap给实现了)调用到。该函数由于是默认的包访问权限,所以一般情况下用户无法调用。 putMapEntries全解析 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {//m的类型参数是? extends,所以只

anlian523的博客 3298

GEE|基于时间序列特征的山东省玉米随机森林分类代码

主题思路: 本实验为二分类实验,分类目标包括:玉米、其他(建筑、水体、水稻、 裸地、森林、小麦、道路、大蒜等),采用人工设计的特征指数的时间序列特征为分类依据,选取分类器为:随机森林分类器,并选取样本集中 70%作为训练样本,30%作为测试样本,通过调整随机森林棵数,调整分类器结构。选取以下参数进行分类: ⚫ 时间尺度:1990 年 4 月 15 日 - 1990 年 9 月 30 日 ⚫ 空间尺度:山东省 ⚫ 影像融合时间间隔:16天 ⚫ 特征值选择:['nir','ndvi'...

weixin_46493028的博客 7467

HashMap的源码初理解

一 定义 基于哈希表的 Map,Cloneable, Serializable接口的实现。与 HashTable 类相似,只不过 HashMap 允许 null 键与 null 值,而且HashMap 是非同步的,HashMap可以通过 Map m = Collections.synchronizeMap(hashMap) 语句进行同步。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。如果...

MR_L_0927的博客 346

避坑指南:锐浪报表PictureBox打印图像常见的5大问题及解决方案

本文深入剖析了锐浪报表(Grid++Report)中PictureBox控件打印图像时常见的五大难题,包括图像源加载机制、拉伸失真、动态加载失败、跨平台兼容性及性能优化。文章提供了从原理理解到实战解决方案的完整指南,帮助开发者有效规避图像打印中的常见陷阱,确保报表输出的稳定与高质量。

water的专栏 1148

Arnold Denoise流程

参考:https://support.solidangle.com/display/ARP/Removing+Noise+Workflow Arnold生来不是一个以单帧渲染速度见长的渲染器,但这并不能作为一个简单场景渲染时间长达几个小时的借口。 个人认为高品质生产级渲染结果中,Unbiased renderer在渲染时最耗计算时间的其实就是降噪,以至于降噪专门成了一门灯光师必备的技术

机器猫的CG空间 5084

Java集合框架解析:从基础到底层源码

单线程环境快速查询:ArrayList/HashMap频繁增删:LinkedList需要排序:TreeSet/TreeMap高并发场景读多写少:CopyOnWriteArrayList写多读少:ConcurrentHashMap严格一致性:Collections.synchronized系列函数式编程使用Stream API进行链式处理优先选择不可变集合(Guava/Java9+)彩蛋知识点。

Hello World!你好世界! 2866

【收藏向】模拟电子技术超强知识点总结 20小时不挂科

模电真的有难度的一门课,一定要好好学… 用的教材是华科康华光版,其他版本教材也可参考,内容是差不多的。 话不多说直接上思维导图干货(后有思维导图高清原图链接) 1 绪论 2 运算放大器 3 二极管及其基本电路 4 场效应管及其放大电路 5 双极结型三极管及其放大电路 三种组态总结看不清,给个特写 发现虽然csdn上传的是原图,但是显示的图片不是特别清晰,在此贴出B站知乎地址,第五章之后的内容都在上面,还可以查看原图 ———————————————————— B站专栏地址(可以点击查看原图) htt

工科狗Niko的博客 1万+

Java魔法解密:HashMap底层机制大揭秘

"Java魔法解密:HashMap底层机制大揭秘"一文深入探讨了Java中HashMap的底层机制。通过解密HashMap的工作原理,读者将了解到HashMap是如何通过哈希表实现快速查找的,同时也揭示了在实际应用中如何避免碰撞并确保性能。本文对于想要深入理解Java集合框架以及底层数据结构的开发人员来说,将提供深入而有益的见解。

忆~遂愿的博客 6519

webapi .net core8.0配置使用Token权限认证

本人在网上找了很多版本,不是太复杂就是不行。

m0_74824592的博客 1760

深入解析MTK DRM初始化流程:从KMS模块到组件框架

本文深入解析了联发科(MTK)平台上DRM显示框架的初始化流程。从内核模块加载、Component框架管理依赖,到核心的mtk_drm_bind函数构建KMS对象(CRTC、Plane、Encoder、Connector),最终完成DRM设备注册并创建用户空间接口。文章结合调试经验,为开发者梳理了从驱动启动到屏幕点亮的完整路径与关键排查点。

onion的博客 767

Java集合之HashMap源码详解

HashMap的源码详细解析

个人博客,欢迎收藏。 1931

【制作100个Unity游戏之35】从零开始构建类《月圆之夜》《杀戮尖塔》的卡牌游戏(附带项目源码)

本文介绍了如何从零开始构建类似《月圆之夜》《杀戮尖塔》的卡牌游戏。主要内容包括:1)获取游戏素材资源;2)导入xyframe框架工具集;3)实现手牌弯曲排列效果,通过样条曲线控制卡牌位置和旋转;4)设计可扩展的MVD卡牌系统架构,分离数据层(ScriptableObject存储)、模型层(业务逻辑)和视图层(显示控制)。文中提供了完整的代码实现,并展示了卡牌动态排列的GIF效果演示。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1429

深入解析:Java 集合框架与源码分析

Java 集合框架由多个接口和类组成,提供了一种存储和操作对象的标准方式。主要接口包括CollectionListSet和Map。所有集合类都实现了这些接口,以便于数据的管理。Java 集合框架提供了丰富的集合类型,开发者应根据实际需求选择合适的集合。理解集合的内部实现和性能特性将有助于写出高效的代码。通过对ArrayListHashSet和HashMap的深入分析,我们可以看到这些集合是如何运作的,以及它们的优势和局限性。希望通过本篇文章,能够帮助你深入理解 Java 集合框架,从而在开发中游刃有余!

一名热衷于技术的全栈开发者,专注于前端与后端的全面技术探索。在这里,我将分享我在技术领域的学习与成长,助力更多开发者的进步。 1281

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Java集合篇之HashMap解析与源码分析

HashMap是Map接口的一个实现类,它基于哈希表实现,允许存储键值对。HashMap通过哈希函数将键映射到哈希表中的某个位置,从而实现快速的查找、插入和删除操作。

庄子兄弟的博客 1900

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

HashMap是如何实现键值对管理的?从底层源码入手,详细解读HashMap的具体实现

本文首先对集合、映射等相关知识进行介绍,对HashMap各个重要变量、常量、构造方法、核心方法、序列化原理等源码进行深入解析,并对:“Map是不是集合?”、“HashMap继承了AbstractMap,为什么还要实现Map接口?”、“为什么table用transient修饰?”、“为什么(e.hash & oldCap) == 0时扩容索引值不变?”等问题进行了分析和解答

种一棵树最好的时间是十年前,其次是现在 1129

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java集合 HashMap 原理解读(含源码解析)

HashMap是Java中基于哈希表实现的Map接口的一个非同步实现类,是以key-value存储形式存在,即主要用来存放键值对。本文系统性梳理和总结了该集合类的几乎所有知识点,进行了源码级的解读。

hrh的博客 1420

Java集合框架源码分析:hashMap

特性描述是否允许为nullkey和value都可以取值为null是否允许重复key重复会覆盖,value允许重复是否有序无序是否线程安全非线程安全JDK1.8之前HashMap是由数组 + 链表结构组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突(两个对象调用的hashCode方法计算的哈希码值一致导致计算的数组索引值相同)而存在(拉链法:解决冲突)。JDK1.8以后(包含JDK1.8)HashMap是由数组+链表+红黑树组成。

玉汝于成 1521

Java集合之HashMap(含源码解析 超详细)

上文讲解了ArrayList,这次我们来看HashMapHashMap 主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一。

rc1596919047的博客 1455

Java集合框架:Set、List、Map及HashMap底层实现详解

Java集合框架主要分为三大类接口:​​List(列表)​​:有序集合,元素可重复实现类:ArrayList、LinkedList、Vector​​Set(集)​​:无序集合,元素不可重复实现类:HashSet、LinkedHashSet、TreeSet​​Map(映射)​​:键值对集合实现类:HashMap、LinkedHashMap、TreeMap、Hashtable集合类底层结构线程安全有序性允许nullArrayList动态数组否插入顺序是LinkedList。

aakhvb的博客 907

Java 集合框架全解析:从底层原理到实战优化

Java集合框架(JCF)是处理对象组的核心工具,解决了数组的局限性,提供动态扩展、通用操作方法和类型安全。其架构分为Collection(单元素)和Map(键值对)两大分支,通过接口与实现分离的设计原则,提供丰富的功能封装和多场景适配。Collection分支包括List(有序可重复)、Set(无序不可重复)和Queue(队列)三大接口,每个接口都有多种实现类满足不同需求。Map分支存储键值对,包括普通Map和并发Map等实现。集合框架通过泛型确保类型安全,并提供Collections和Arrays工具类

手心朝下的博客 1073
上一篇: Java集合框架|Map体系和Collections工具类讲解
下一篇: Java中IO流详细整合(含案例)
I'm Jasper
博客等级 码龄8年 4粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值