03.Java数据结构问题

目录介绍

  • 3.0.0.1 在arrayList中System.arraycopy()和Arrays.copyOf()方法区别联系?System.arraycopy()和Arrays.copyOf()代码说明?
  • 3.0.0.2 SparseArray基本介绍,相比HashMap为何性能会好?
  • 3.0.0.3 Arrays和Collections 对于sort的不一样实现原理?说一说它们的区别……
  • 3.0.0.4 Java集合框架中有哪些类?都有什么特色?Java集合的快速失败机制 “fail-fast”?
  • 3.0.0.5 ArrayList,Vector和LinkList的区别,底层分别是怎么实现的,存储空间是如何扩容的?什么是加载因子?
  • 3.0.0.6 如何理解ArrayList的扩容消耗?Arrays.asList方法后的List能够扩容吗?ArrayList如何序列化?
  • 3.0.0.7 如何理解list集合读写机制和读写效率?什么是CopyOnWriteArrayList,它与ArrayList有何不一样?
  • 3.0.1.0 HashSet和TreeSet的区别?是如何保证惟一值的,底层怎么作到的?
  • 3.0.1.5 HashMap和Hashtable的区别?HashMap在put、get元素的过程?体现了什么数据结构?
  • 3.0.1.6 如何保证HashMap线程安全?底层怎么实现的?HashMap是有序的吗?如何实现有序?
  • 3.0.1.7 HashMap存储两个对象的hashcode相同会发生什么?若是两个键的hashcode相同,你如何获取值对象?
  • 3.0.1.8 HashMap为何不直接使用hashCode()处理后的哈希值直接做为table的下标?
  • 3.0.1.9 为何HashMap中String、Integer这样的包装类适合做为K?为啥不用其余做key值?
  • 3.0.2.0 HashMap是如何扩容的?如何理解HashMap的大小超过了负载因子定义的容量?从新调整HashMap大小存在什么问题吗?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括Java基础及深刻知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,固然也在工做之余收集了大量的面试题,长期更新维护而且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 连接地址:https://github.com/yangchong211/YCBlogs
  • 若是以为好,能够star一下,谢谢!固然也欢迎提出建议,万事起于忽微,量变引发质变!全部博客将陆续开源到GitHub!

3.0.0.1 在arrayList中System.arraycopy()和Arrays.copyOf()方法区别联系?System.arraycopy()和Arrays.copyOf()代码说明?

  • System.arraycopy()和Arrays.copyOf()方法区别?
    • 好比下面<font color="red">add(int index, E element)</font>方法就很巧妙的用到了<font color="red">arraycopy()方法</font>让数组本身复制本身实现让index开始以后的全部成员后移一个位置:
      /**
       * 在此列表中的指定位置插入指定的元素。 
       * 先调用 rangeCheckForAdd 对index进行界限检查;而后调用 ensureCapacityInternal 方法保证capacity足够大;
       * 再将从index开始以后的全部成员后移一个位置;将element插入index位置;最后size加1。
       */
      public void add(int index, E element) {
          rangeCheckForAdd(index);
          ensureCapacityInternal(size + 1); 
          //arraycopy()方法实现数组本身复制本身
          //elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量;
          System.arraycopy(elementData, index, elementData, index + 1, size - index);
          elementData[index] = element;
          size++;
      }
    • 如toArray()方法中用到了copyOf()方法
      /**
       *以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中全部元素的数组。 
       *返回的数组将是“安全的”,由于该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。
       *所以,调用者能够自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。
       */
      public Object[] toArray() {
      //elementData:要复制的数组;size:要复制的长度
          return Arrays.copyOf(elementData, size);
      }
    • 二者联系与区别
      • 看了上面的二者源代码能够发现copyOf()内部调用了System.arraycopy()方法
      • 技术博客大总结
      • 区别:
        • 1.arraycopy()须要目标数组,将原数组拷贝到你本身定义的数组里,并且能够选择拷贝的起点和长度以及放入新数组中的位置
        • 2.copyOf()是系统自动在内部新建一个数组,并返回该数组。
  • System.arraycopy()和Arrays.copyOf()代码说明?php

    • 使用System.arraycopy()方法java

      public static void main(String[] args) {
          // TODO Auto-generated method stub
          int[] a = new int[10];
          a[0] = 0;
          a[1] = 1;
          a[2] = 2;
          a[3] = 3;
          System.arraycopy(a, 2, a, 3, 3);
          a[2]=99;
          for (int i = 0; i < a.length; i++) {
              System.out.println(a[i]);
          }
      }
      
      //结果:
      //0 1 99 2 3 0 0 0 0 0
    • 使用Arrays.copyOf()方法。技术博客大总结
      public static void main(String[] args) {
          int[] a = new int[3];
          a[0] = 0;
          a[1] = 1;
          a[2] = 2;
          int[] b = Arrays.copyOf(a, 10);
          System.out.println("b.length"+b.length);
          //结果:
          //10
      }
    • 得出结论
      • arraycopy() 须要目标数组,将原数组拷贝到你本身定义的数组里或者原数组,并且能够选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组,并返回该数组。

3.0.0.2 SparseArray基本介绍,相比HashMap为何性能会好?

  • 位于android.util,Android 中的数据结构,针对移动端作了优化,在数据量比较少的状况下,性能会好过 HashMap,相似于 HashMap,key:int ,value:object 。
  • 1.key 和 value 采用数组进行存储。存储 key 的数组是 int 类型,不须要进行装箱操做。提供了速度。
  • 2.采用二分查找法,在插入进行了排序,因此两个数组是按照从小到大进行排序的。
  • 3.在查找的时候,进行二分查找,数据量少的状况下,速度比较快。

3.0.0.3 Arrays和Collections 对于sort的不一样实现原理?说一说它们的区别……

  • 一、Arrays.sort()
    • 该算法是一个通过调优的快速排序,此算法在不少数据集上提供N*log(N)的性能,这致使其余快速排序会下降二次型性能。
  • 二、Collections.sort()
    • 该算法是一个通过修改的合并排序算法(其中,若是低子列表中的最高元素效益高子列表中的最低元素,则忽略合并)。此算法可提供保证的N*log(N)的性能,此实现将指定列表转储到一个数组中,而后再对数组进行排序,在重置数组中相应位置处每一个元素的列表上进行迭代。
  • 它们的区别

3.0.0.4 Java集合框架中有哪些类?都有什么特色?Java集合的快速失败机制 “fail-fast”?

  • 可将Java集合框架大体可分为Set、List、Queue 和Map四种体系
    • Set:表明无序、不可重复的集合,常见的类如HashSet、TreeSet
    • List:表明有序、可重复的集合,常见的类如动态数组ArrayList、双向链表LinkedList、可变数组Vector
    • Map:表明具备映射关系的集合,常见的类如HashMap、LinkedHashMap、TreeMap
    • Queue:表明一种队列集合
  • Java集合的快速失败机制 “fail-fast”
    • 技术博客大总结
    • java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操做时,有可能会产生 fail-fast 机制。
      • 例如:假设存在两个线程(线程一、线程2),线程1经过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
    • 缘由:
      • 迭代器在遍历时直接访问集合中的内容,而且在遍历过程当中使用一个 modCount 变量。集合在被遍历期间若是内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素以前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;不然抛出异常,终止遍历。
    • 解决办法:
      • 1.在遍历过程当中,全部涉及到改变modCount值得地方所有加上synchronized。
      • 2.使用CopyOnWriteArrayList来替换ArrayList

3.0.0.5 ArrayList,Vector和LinkList的区别,底层分别是怎么实现的?存储空间是如何扩容的?什么是加载因子?

  • ArrayList
    • ArrayList的底层结构是数组,可用索引实现快速查找;是动态数组,相比于数组容量可实现动态增加。
    • ArrayList非线程安全,建议在单线程中才使用ArrayList,而在多线程中能够选择Vector或者CopyOnWriteArrayList;默认初始容量为10,每次扩容为原来的1.5倍
  • Vector
    • 和ArrayList几乎是同样的,Vector使用了synchronized关键字,是线程安全的,比ArrayList开销更大,访问更慢;默认初始容量为10,默认每次扩容为原来的2倍,可经过capacityIncrement属性设置
  • LinkList
    • LinkedList底层结构是链表,增删速度快;是一个双向循环链表,也能够被看成堆栈、队列或双端队列

3.0.0.6 如何理解ArrayList的扩容消耗?Arrays.asList方法后的List能够扩容吗?ArrayList如何序列化?

  • 如何理解ArrayList的扩容消耗
    • 因为ArrayList使用elementData = Arrays.copyOf(elementData, newCapacity);进行扩容,而每次都会从新建立一个newLength长度的数组,因此扩容的空间复杂度为O(n),时间复杂度为O(n)
      public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
      T[] copy = ((Object)newType == (Object)Object[].class)
          ? (T[]) new Object[newLength]
          : (T[]) Array.newInstance(newType.getComponentType(), newLength);
      System.arraycopy(original, 0, copy, 0,
                       Math.min(original.length, newLength));
      return copy;
      }
  • Arrays.asList方法后的List能够扩容吗?
    • 不能,asList返回的List为只读的。其缘由为:asList方法返回的ArrayList是Arrays的一个内部类,而且没有实现add,remove等操做
  • List怎么实现排序?
    • 实现排序,可使用自定义排序:list.sort(new Comparator(){...})
    • 或者使用Collections进行快速排序:Collections.sort(list)
  • ArrayList如何序列化?android

    • ArrayList 基于数组实现,而且具备动态扩容特性,所以保存元素的数组不必定都会被使用,那么就不必所有进行序列化。
    • 技术博客大总结
    • 保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。
      transient Object[] elementData; // non-private to simplify nested class access
    • ArrayList 实现了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部份内容。
      private void readObject(java.io.ObjectInputStream s)
      throws java.io.IOException, ClassNotFoundException {
      elementData = EMPTY_ELEMENTDATA;
      s.defaultReadObject();
      s.readInt(); // ignored
      if (size > 0) {
          ensureCapacityInternal(size);
          Object[] a = elementData;
          for (int i=0; i<size; i++) {
              a[i] = s.readObject();
          }
      }
      }

    private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    int expectedModCount = modCount;
    s.defaultWriteObject();
    s.writeInt(size);
    for (int i=0; i<size; i++) {
    s.writeObject(elementData[i]);
    }
    if (modCount != expectedModCount) {
    throw new ConcurrentModificationException();
    }
    }git

    - 序列化时须要使用 ObjectOutputStream 的 writeObject() 将对象转换为字节流并输出。而 writeObject() 方法在传入的对象存在 writeObject() 的时候会去反射调用该对象的 writeObject() 来实现序列化。反序列化使用的是 ObjectInputStream 的 readObject() 方法,原理相似。

    ArrayList list = new ArrayList();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
    oos.writeObject(list);github

     

3.0.0.7 如何理解list集合读写机制和读写效率?什么是CopyOnWriteArrayList,它与ArrayList有何不一样?

  • 读写机制
    • ArrayList在执行插入元素是超过当前数组预约义的最大值时,数组须要扩容,扩容过程须要调用底层System.arraycopy()方法进行大量的数组复制操做;在删除元素时并不会减小数组的容量(若是须要缩小数组容量,能够调用trimToSize()方法);在查找元素时要遍历数组,对于非null的元素采起equals的方式寻找。
    • LinkedList在插入元素时,须建立一个新的Entry对象,并更新相应元素的先后元素的引用;在查找元素时,需遍历链表;在删除元素时,要遍历链表,找到要删除的元素,而后从链表上将此元素删除便可。
    • Vector与ArrayList仅在插入元素时容量扩充机制不一致。对于Vector,默认建立一个大小为10的Object数组,并将capacityIncrement设置为0;当插入元素数组大小不够时,若是capacityIncrement大于0,则将Object数组的大小扩大为现有size+capacityIncrement;若是capacityIncrement<=0,则将Object数组的大小扩大为现有大小的2倍。
  • 读写效率
    • ArrayList对元素的增长和删除都会引发数组的内存分配空间动态发生变化。所以,对其进行插入和删除速度较慢,但检索速度很快。
    • LinkedList因为基于链表方式存放数据,增长和删除元素的速度较快,可是检索速度较慢。
  • 什么是CopyOnWriteArrayList,它与ArrayList有何不一样?
    • CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中全部可变操做(add、set等等)都是经过对底层数组进行一次新的复制来实现的。相比较于ArrayList它的写操做要慢一些,由于它须要实例的快照。
    • CopyOnWriteArrayList中写操做须要大面积复制数组,因此性能确定不好,可是读操做由于操做的对象和写操做不是同一个对象,读之间也不须要加锁,读和写之间的同步处理只是在写完后经过一个简单的"="将引用指向新的数组对象上来,这个几乎不须要时间,这样读操做就很快很安全,适合在多线程里使用,绝对不会发生ConcurrentModificationException ,所以CopyOnWriteArrayList适合使用在读操做远远大于写操做的场景里,好比缓存。
    • 技术博客大总结

3.0.1.0 HashSet和TreeSet的区别?是如何保证惟一值的,底层怎么作到的?

  • HashSet
    • 不能保证元素的排列顺序;使用Hash算法来存储集合中的元素,有良好的存取和查找性能;经过equal()判断两个元素是否相等,并两个元素的hashCode()返回值也相等
  • TreeSet
    • 是SortedSet接口的实现类,根据元素实际值的大小进行排序;采用红黑树的数据结构来存储集合元素;支持两种排序方法:天然排序(默认状况)和定制排序。前者经过实现Comparable接口中的compareTo()比较两个元素之间大小关系,而后按升序排列;后者经过实现Comparator接口中的compare()比较两个元素之间大小关系,实现定制排列

3.0.1.5 HashMap和Hashtable的区别?HashMap在put、get元素的过程?体现了什么数据结构?

  • HashMap
    • 基于AbstractMap类,实现了Map、Cloneable(能被克隆)、Serializable(支持序列化)接口; 非线程安全;容许存在一个为null的key和任意个为null的value;采用链表散列的数据结构,即数组和链表的结合;初始容量为16,填充因子默认为0.75,扩容时是当前容量翻倍,即2capacity
  • Hashtable
    • 基于Map接口和Dictionary类;线程安全,开销比HashMap大,若是多线程访问一个Map对象,使用Hashtable更好;不容许使用null做为key和value;底层基于哈希表结构;初始容量为11,填充因子默认为0.75,扩容时是容量翻倍+1,即2capacity+1
    • HashTable里使用的是synchronized关键字,这实际上是对对象加锁,锁住的都是对象总体,当Hashtable的大小增长到必定的时候,性能会急剧降低,由于迭代时须要被锁定很长的时间。
  • HashMap在put、get元素的过程
    • 向Hashmap中put元素时,首先判断key是否为空,为空则直接调用putForNullKey(),不为空则计算key的hash值获得该元素在数组中的下标值;若是数组在该位置处没有元素,就直接保存;若是有,还要比较是否存在相同的key,存在的话就覆盖原来key的value,不然将该元素保存在链头,先保存的在链尾。
    • 从Hashmap中get元素时,计算key的hash值找到在数组中的对应的下标值,返回该key对应的value便可,若是有冲突就遍历该位置链表寻找key相同的元素并返回对应的value
  • 体现了什么数据结构
    • HashMap采用链表散列的数据结构,即数组和链表的结合,在Java8后又结合了红黑树,当链表元素超过8个将链表转换为红黑树
    • 技术博客大总结

3.0.1.6 如何保证HashMap线程安全?底层怎么实现的?HashMap是有序的吗?如何实现有序?

  • 使用ConcurrentHashMap可保证线程安全
    • ConcurrentHashMap是线程安全的HashMap,它采起锁分段技术,将数据分红一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其余段的数据也能被其余线程访问。在JDK1.8中对ConcurrentHashmap作了两个改进:
      • 取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,将数组元素做为锁,对每一行数据进行加锁,可减小并发冲突的几率
      • 数据结构由“数组+单向链表”变为“数组+单向链表+红黑树”,使得查询的时间复杂度能够下降到O(logN),改进必定的性能。
    • 通俗一点解释:ConcurrentHashMap引入了分割(Segment),能够理解为把一个大的Map拆分红N个小的HashTable,在put方法中,会根据hash(paramK.hashCode())来决定具体存放进哪一个Segment,若是查看Segment的put操做,咱们会发现内部使用的同步机制是基于lock操做的,这样就能够对Map的一部分(Segment)进行上锁,这样影响的只是将要放入同一个Segment的元素的put操做,保证同步的时候,锁住的不是整个Map(HashTable就是这么作的),相对于HashTable提升了多线程环境下的性能,所以HashTable已经被淘汰了。技术博客大总结
  • 使用LinkedHashMap可实现有序
    • HashMap是无序的,而LinkedHashMap是有序的HashMap,默认为插入顺序,还能够是访问顺序,基本原理是其内部经过Entry维护了一个双向链表,负责维护Map的迭代顺序

3.0.1.7 HashMap存储两个对象的hashcode相同会发生什么?若是两个键的hashcode相同,你如何获取值对象?

  • HashMap存储两个对象的hashcode相同会发生什么?
    • 错误回答:由于hashcode相同,因此两个对象是相等的,HashMap将会抛出异常,或者不会存储它们。
    • 正确回答:两个对象就算hashcode相同,可是它们可能并不相等。若是不明白,能够先看看个人这篇博客:Hash和HashCode深刻理解。回答“由于hashcode相同,因此它们的bucket位置相同,‘碰撞’会发生。由于HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。
  • HashMap1.7和1.8的区别
    • 在JDK1.6,JDK1.7中,HashMap采用数组+链表实现,即便用链表处理冲突,同一hash值的链表都存储在一个链表里。可是当位于一个链表中的元素较多,即hash值相等的元素较多时,经过key值依次查找的效率较低。
    • JDK1.8中,HashMap采用位数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减小了查找时间。
  • 若是两个键的hashcode相同,你如何获取值对象?
    • 当调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,而后获取值对象。固然若是有两个值对象储存在同一个bucket,将会遍历链表直到找到值对象。
    • 在没有值对象去比较,如何肯定肯定找到值对象的?由于HashMap在链表中存储的是键值对,找到bucket位置以后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。
    • 技术博客大总结

3.0.1.8 HashMap为何不直接使用hashCode()处理后的哈希值直接做为table的下标?

  • 不直接使用hashCode()处理后的哈希值
    • hashCode()方法返回的是int整数类型,其范围为-(2^31)~(2^31-1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap一般状况下是取不到最大值的,而且设备上也难以提供这么多的存储空间,从而致使经过hashCode()计算出的哈希值可能不在数组大小范围内,进而没法匹配存储位置;
  • HashMap是使用了哪些方法来有效解决哈希冲突的
    • 1.使用链地址法(使用散列表)来连接拥有相同hash值的数据;
    • 2.使用2次扰动函数(hash函数)来下降哈希冲突的几率,使得数据分布更平均;
    • 3.引入红黑树进一步下降遍历的时间复杂度,使得遍历更快;
  • 如何解决匹配存储位置问题
    • HashMap本身实现了本身的hash()方法,经过两次扰动使得它本身的哈希值高低位自行进行异或运算,下降哈希碰撞几率也使得数据分布更平均;
    • 在保证数组长度为2的幂次方的时候,使用hash()运算以后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操做更加有效率,二来也是由于只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题;
  • 为何数组长度要保证为2的幂次方呢?
    • 只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,即实现了key的定位,2的幂次方也能够减小冲突次数,提升HashMap的查询效率;
    • 技术博客大总结
    • 若是 length 为 2 的次幂 则 length-1 转化为二进制一定是 11111……的形式,在于 h 的二进制与操做效率会很是的快,并且空间不浪费;若是 length 不是 2 的次幂,好比 length 为 15,则 length - 1 为 14,对应的二进制为 1110,在于 h 与操做,最后一位都为 0 ,而 0001,0011,0101,1001,1011,0111,1101 这几个位置永远都不能存放元素了,空间浪费至关大,更糟的是这种状况中,数组可使用的位置比数组长度小了不少,这意味着进一步增长了碰撞的概率,减慢了查询的效率!这样就会形成空间的浪费。

3.0.1.9 为何HashMap中String、Integer这样的包装类适合做为K?为啥不用其余做key值?

  • 为何HashMap中String、Integer这样的包装类适合做为K?
    • String、Integer等包装类的特性可以保证Hash值的不可更改性和计算准确性,可以有效的减小Hash碰撞的概率
      • 都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不一样的状况
      • 内部已重写了equals()、hashCode()等方法,遵照了HashMap内部的规范(不清楚能够去上面看看putValue的过程),不容易出现Hash值计算错误的状况;
  • 想要让本身的Object做为K应该怎么办呢?
    • 重写hashCode()和equals()方法
      • 重写hashCode()是由于须要计算存储数据的存储位置,须要注意不要试图从散列码计算中排除掉一个对象的关键部分来提升性能,这样虽然能更快但可能会致使更多的Hash碰撞;
      • 重写equals()方法,须要遵照自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的惟一性;
  • 总结
    • 采用合适的equals()和hashCode()方法的话,将会减小碰撞的发生,提升效率。不可变性使得可以缓存不一样键的hashcode,这将提升整个获取对象的速度,使用String,Interger这样的wrapper类做为键是很是好的选择。
    • 技术博客大总结

3.0.2.0 HashMap是如何扩容的?如何理解HashMap的大小超过了负载因子定义的容量?从新调整HashMap大小存在什么问题吗?

  • HashMap是为啥要扩容
    • 当链表数组的容量超过初始容量*加载因子(默认0.75)时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中。为何须要使用加载因子?为何须要扩容呢?由于若是填充比很大,说明利用的空间不少,若是一直不进行扩容的话,链表就会愈来愈长,这样查找的效率很低,扩容以后,将原来链表数组的每个链表分红奇偶两个子链表分别挂在新链表数组的散列位置,这样就减小了每一个链表的长度,增长查找效率。
  • 如何理解HashMap的大小超过了负载因子(load factor)定义的容量?
    • 默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)同样,将会建立原来HashMap大小的两倍的bucket数组,来从新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫做rehashing,由于它调用hash方法找到新的bucket位置。
  • 从新调整HashMap大小存在什么问题吗?技术博客大总结
    • 当多线程的状况下,可能产生条件竞争。当从新调整HashMap大小的时候,确实存在条件竞争,由于若是两个线程都发现HashMap须要从新调整大小了,它们会同时试着调整大小。在调整大小的过程当中,存储在链表中的元素的次序会反过来,由于移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了不尾部遍历(tail traversing)。若是条件竞争发生了,那么就死循环了。

其余介绍

01.关于博客汇总连接

02.关于个人博客

相关文章
相关标签/搜索