JAVA中HashMap、ArrayList、StringBuilder等的扩容机制

原文地址:https://www.cnblogs.com/lq147760524/p/6713677.html

第一部分:
  • HashMap hmap=new HashMap<>();
  • HashSet hset=new HashSet<>();
  • Hashtable htable=new Hashtable<>();
第二部分:
  • CopyOnWriteArrayList coarray=new CopyOnWriteArrayList<>();
  • ArrayList array=new ArrayList<>();
  • Vector vec=new Vector<>();
第三部分:
  • StringBuffer sb=new StringBuffer();
  • StringBuilder sbu=new StringBuilder();

从以下几个源码方面分析:(JDK1.8)

  1. 初始容量。
  2. 扩容机制。
  3. 同类型之间对比。

1.1 HashMap

初始容量定义:默认为1 << 4(16)。最大容量为1<< 30。

/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;

扩容加载因子为(0.75),第一个临界点在当HashMap中元素的数量等于table数组长度加载因子(160.75=12),如果超出则按oldThr << 1(原长度*2)扩容。

/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Constructs an empty HashMap with the default initial capacity
* (16) and the default load factor (0.75).
*/
if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
    newThr = oldThr << 1; // double threshold

1.2 HashSet

初始容量定义:16。因为构造一个HashSet,其实相当于新建一个HashMap,然后取HashMap的Key。扩容机制和HashMap一样。

public HashSet(Collection c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

1.3 HashTable

Hashtable htable=new Hashtable<>();
public class Hashtable extends Dictionary

初始容量定义:capacity (11)。

/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
    this(11, 0.75f);
}

扩容加载因子(0.75),当超出默认长度(int)(110.75)=8时,扩容为old2+1。

int newCapacity = (oldCapacity << 1) + 1;

HashTable和HashMap区别

  1. 继承不同。
public class Hashtable extends Dictionary implements Map
public class HashMap extends AbstractMap implements Map
  1. Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。 在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
  2. Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
  3. 两个遍历方式的内部实现上不同。 Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
  4. 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
  5. Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式不同。 HashTable中hash数组默认大小是11,增加的方式是 old2+1。HashMap中hash数组的默认大小是16, 增加的方式是 old2。

2.1 CopyOnWriteArrayList

/**
* Creates an empty list.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}

CopyOnWriteArrayList在做修改操作时,每次都是重新创建一个新的数组,在新数组上操作,最终再将新数组替换掉原数组。因此,在做修改操作时,仍可以做读取操作,读取直接操作的原数组。读和写操作的对象都不同,因此读操作和写操作互不干扰。只有写与写之间需要进行同步等待。另外,原数组被声明为volatile,这就保证了,一旦数组发生变化,则结果对其它线程(读线程和其它写线程)是可见的。

CopyOnWriteArrayList并不像ArrayList一样指定默认的初始容量。它也没有自动扩容的机制,而是添加几个元素,长度就相应的增长多少。

CopyOnWriteArrayList适用于读多写少,既然是写的情况少,则不需要频繁扩容。并且修改操作每次在生成新的数组时就指定了新的容量,也就相当于扩容了,所以不需要额外的机制来实现扩容。

2.2 ArrayList<String> array=new ArrayList<>();

初始容量定义:10。

/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
  • 扩容:oldCapacity + (oldCapacity >> 1),即原集合长度的1.5倍。
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

2.3 Vector<String> vec=new Vector<>();

  • 初始容量定义:10。
public Vector() {
this(10);
}

扩容:当扩容因子大于0时,新数组长度为原数组长度+扩容因子,否则新数组长度为原数组长度的2倍。

// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);

小结:

  1. ArrayList与Vector初始容量都为10。

  2. 扩容机制不同,当超出当前长度时ArrayList扩展为原来的1.5倍,而若不考虑扩容因子Vector扩展为原来的2倍。

  3. ArrayList为非线程安全的,处理效率上较Vector快,若同时考虑线程安全和效率,可以使用 CopyOnWriteArrayList。

3.1 StringBuffer sb=new StringBuffer();

初始容量定义:16。

public StringBuffer() {
super(16);
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

扩容,因为StringBuffer extends AbstractStringBuilder,所以实际上是用的是AbstractStringBuilder的扩容方法,当用append(str),添加字符串时,假设字符串中已有字符长度为count的字符串,初始长度value=16,若要添加的字符串长度(count+str.length())<=(value2+2)则按value2+2长度扩容,并且value=value2+2,若(count+str.length())>(value2+2),则按count+str.length()长度扩容,并且value=count+str.length()。下次超出时再按以上方法与value*2+2比较扩容。

private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;

3.2 StringBuilder sbu=new StringBuilder();

public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public StringBuilder() {
super(16);
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;

小结:

  1. StringBuilder是jdk1.5引进的,而StringBuffer在1.0就有了;
  2. StringBuilder和StringBuffer都是可变的字符串。能够通过append或者insert等方法改动串的内容;
  3. StringBuffer是线程安全的而StringBuilder不是,因而在多线程的环境下优先使用StringBuffer,而其它情况下推荐使用StringBuilder,由于它更快。
  4. StringBuilder和StringBuffer都继承自AbstractStringBuilder类,AbStractStringBuilder主要实现了扩容、append、insert方法。StrngBuilder和StringBuffer的相关方法都直接调用的父类。
  5. StringBuilder和StringBuffer的初始容量都是16,程序猿尽量手动设置初始值。以避免多次扩容所带来的性能问题;
  6. StringBuilder和StringBuffer的扩容机制是这种:首先试着将当前数组容量扩充为原数组容量的2倍加上2,假设这个新容量仍然小于预定的最小值(minimumCapacity),那么就将新容量定为(minimumCapacity),最后推断是否溢出,若溢出,则将容量定为整型的最大值0x7fffffff。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容