java 字符串 扩容 16_java中的一系列扩容问题

扩容问题

类名

增长速率

初始值

ArrayList

1.5x+1

默认10

Vector

2x(如果有增量y,x+y)

默认10

HashTable

2x+1

默认11

HashMap

2x

默认16

StringBuffer

2x+2

默认16

StringBuilder

2x+2

默认16

ArrayList(顺序表)

​ List接口的可调整大小的数组实现。

==底层由数组实现,内存连续,默认初始容量为10,默认底==

==层是根据右移运算来扩容是在原来的基础上增加一半。增==

==删效率较低、查询效率较高。线程不安全的集合==

增删慢,查询快

线程不安全

构造函数的初始容量大小是10

内存连续

扩容:(JDK 1.9)根据右移运算来扩容是在原来的基础上增加一半。

其没有自己的缩容方法,是依赖系统数组的缩容

private int newCapacity(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + (oldCapacity >> 1); //1.5倍

if (newCapacity - minCapacity <= 0) {

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)

return Math.max(DEFAULT_CAPACITY, minCapacity);

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return minCapacity;

}

return (newCapacity - MAX_ARRAY_SIZE <= 0)

? newCapacity

: hugeCapacity(minCapacity);

}

private Object[] grow(int minCapacity) {

return elementData = Arrays.copyOf(elementData,

newCapacity(minCapacity));

}

//将此 ArrayList 实例的容量调整为列表的当前大小。

public void trimToSize() {

modCount++;

if (size < elementData.length) {

elementData = (size == 0)

? EMPTY_ELEMENTDATA

: Arrays.copyOf(elementData, size);

}

}

思考题:在查询和增删操作相同的场景下, 选LinkedList还是

ArrayList?

此场景时间差不多。决定效率高低的是占用的空间,考虑空间,LinkedList内存不连续==不需要考虑扩容==,所以占用空间更少,不会产生浪费,更佳。

Vector(JDK1.0)

Vector类实现了一个可生长的数组对象。

底层基于数组实现

==默认初始容量是10,默认的扩容是根据底层的三目运算为原来的两倍==

==如果指定了增量大小,每次扩容就是在原来的基础上加上增量的值。==

最早的集合类

线程安全

protected int capacityIncrement; //容量的增量

//指定增量的空向量

public Vector(int initialCapacity, int capacityIncrement) {

super();

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal Capacity: "+

initialCapacity);

this.elementData = new Object[initialCapacity];

this.capacityIncrement = capacityIncrement;

}

private Object[] grow(int minCapacity) {

return elementData = Arrays.copyOf(elementData,

newCapacity(minCapacity));

}

private Object[] grow() {

return grow(elementCount + 1);

}

private int newCapacity(int minCapacity) {

// overflow-conscious code

int oldCapacity = elementData.length;

int newCapacity = oldCapacity + ((capacityIncrement > 0) ?

capacityIncrement : oldCapacity);

if (newCapacity - minCapacity <= 0) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return minCapacity;

}

return (newCapacity - MAX_ARRAY_SIZE <= 0)

? newCapacity

: hugeCapacity(minCapacity);

}

//对此向量的容量进行微调,使其等于向量的当前大小。

public synchronized void trimToSize() {

modCount++;

int oldCapacity = elementData.length;

if (elementCount < oldCapacity) {

elementData = Arrays.copyOf(elementData, elementCount);

}

}

//用于遍历,返回最古老的迭代器 ,只存在于Voctor

public Enumeration elements() {

return new Enumeration() {

int count = 0;

public boolean hasMoreElements() { //放在while循环,判断是否还有元素

return count < elementCount;

}

public E nextElement() { //用于获取元素

synchronized (Vector.this) {

if (count < elementCount) {

return elementData(count++);

}

}

throw new NoSuchElementException("Vector Enumeration");

}

};

}

HashSet

public class HashSet extends AbstractSetimplements Set, Cloneable, Serializable

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

底层是由HashMap(数组+单链表)实现

构造方法摘要

HashSet() 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 ==16==,加载因子是 ==0.75==。

HashSet(Collection extends E> c) 构造一个包含指定 collection 中的元素的新 set。

HashSet(int initialCapacity) 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。

HashSet(int initialCapacity, float loadFactor) 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

public HashSet(Collection extends E> c) {

map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));

addAll(c);

}

扩容:(Jdk1.7)

==加载因子:如果已经使用的桶数/总桶数大于加载因子(0.75),默认就需要扩容为原来的两倍。==

==扩容完之后需要保证还是散列分布,重写计算原来已经存储的对象的二次运算,重新分布-------rehash会重新二次运算哈希值==

如果加载因子越大,导致大量的元素对象存储在某些链上,导致链的长度过长,就对元素的查询降低效率

如果加载因子越小,导致会进行频繁的扩容和rehash操作,浪费大量的内存空间。

JDK1.8之后,如果某个桶太长,超过==8==个,将链表扭转成一个二叉树(==红黑树==),提高查询效率

1574247788958.png

StringBuffer

jdk1.0

线程安全

父类是AbstractStringBuilder,

构造函数可以看到是直接调用父类的构造函数,初始容量设为==16==

public StringBuffer() {

super(16);

}

public StringBuffer(int capacity) {

super(capacity);

}

public StringBuffer(String str) {

super(str.length() + 16);

append(str);

}

public StringBuffer(CharSequence seq) {

this(seq.length() + 16);

append(seq);

}

StringBuilder

jdk1.5

线程不安全

父类是AbstractStringBuilder

构造函数可以看到是直接调用父类的构造函数,初始容量设为==16==

public StringBuilder() {

super(16);

}

public StringBuilder(int capacity) {

super(capacity);

}

public StringBuilder(String str) {

super(str.length() + 16);

append(str);

}

public StringBuilder(CharSequence seq) {

this(seq.length() + 16);

append(seq);

}

其中StringBuffer和StringBuilder的父类都是AbstractStringBuilder,其中需要扩容的操作都是直接调用父类的方法,所以父类的扩容就是他们的扩容方案

AbstractStringBuilder中的扩容:==原容量左移一位+2,先尝试将容量夸大至2倍+2,如果还是不够,则直接扩容至需要的大小==(jdk1.8)

新建了一个数组,将原来旧数组的内容复制到新数组,扩容机制根据当前数组长度的2倍+2和新增加字符串长度+原有数组长度进行比较,如果前者小于后者,那么扩容后的长度就是后者,如果前者大于后者那么扩容后的数组长度就是前者,每次append或者insert会再次进行比较.

一般拼接字符串时都会以","加空格来分隔,字符数组扩容之后是为了存放很多的要拼接的内容,但是又不想因为","和空格这两个字符而扩容所以每次加了2个

/**

* The count is the number of characters used.

*/

int count;

//从append开始

public AbstractStringBuilder append(char[] str) {

int len = str.length;

ensureCapacityInternal(count + len);

appendChars(str, 0, len); //len=str.length

return this;

}

public AbstractStringBuilder append(String str) {

if (str == null) {

return appendNull();

}

int len = str.length();

ensureCapacityInternal(count + len);

putStringAt(count, str);

count += len;

return this;

}

//确保内部容量,用在append等方法中

private void ensureCapacityInternal(int minimumCapacity) {

// overflow-conscious code

int oldCapacity = value.length >> coder;

if (minimumCapacity - oldCapacity > 0) {

value = Arrays.copyOf(value,

newCapacity(minimumCapacity) << coder);

}

}

//扩容操作,原容量左移一位+2

private int newCapacity(int minCapacity) {

// overflow-conscious code

int oldCapacity = value.length >> coder;

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

if (newCapacity - minCapacity < 0) {

newCapacity = minCapacity;

}

int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;

return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)

? hugeCapacity(minCapacity)

: newCapacity;

}

你可能感兴趣的:(java,字符串,扩容,16)