String、ArrayList可以看做是存储数据的容器,底层都是使用数组来存储数据的
String
String底层存储数据是char[ ] value
当空参构造器创建对象时
public String() {
this.value = "".value;
}
此时char[ ] value = new char[ 0 ];
这个一般都不使用,感觉没多大意义。
常见的是通过有参构造器来创建对象
比如:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
当 执行代码 String str = new String("123");时,对象str的char[ ] value = new char[]{'1','2','3'};
StringBuffer
和String一样,StringBuffer底层存储数据也是char[ ] value
当空参构造器创建对象时
public StringBuffer() {
super(16);
}
调用父类构造器
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
也就是char[] value = new char[16];
怎么去记忆16这个数字呢?
因为char是2byte(字节)=16bit (位)
接着如果使用有参构造器创建对象,比如:
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
调用父类的append(str)方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
一眼就能看出来了,char[] value = new char[ str.length + 16];
当value数组容量不够存储时,就会扩容
(value.length << 1) + 2;
原有的数组容量*2倍+2作为新数组的容量,再将原数组的所有元素都复制到新数组中。
str.getChars(0, len, value, count);
将参数字符串添加到value数组中的最末尾。
StringBuffer是可变的,它有一个非常常用的方法,就是append(String str),它也是调用父类的重载方法,在上面也出现过。
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
注意它一直返回的都是调用这个方法的对象本身,所以可以一直调用append(String str);挺方便的。
StringBuffer sb = new StringBuffer();
System.out.println(sb.length());//0
StringBuffer sb2 = sb.append("123");
System.out.println(sb == sb2);//true 内存地址相同,说明引用指向同一个StringBuffer对象
System.out.println(sb.length());//3
StringBuilder
与StringBuffer的底层逻辑一样,只不过StringBuffer底层使用了synchronized 关键字修饰方法,保证了线程安全,但效率就降低了。
而StringBuilder是线程不安全的,效率高一些。
ArrayList
ArrayList底层存储数据是Object[ ] elementData;
最常用的就是调用空参构造器创建对象ArrayList list = new ArrayList();
- Java的JDK7及之前
public ArrayList() {
this(10);
}
public ArrayList(int initialCapacity) {
...
this.elementData = new Object[initialCapacity];
}
使用空参构造器创建对象,底层Object[ ] elementData = new Object[10];
然后添加数据,调用add()
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
...
if (minCapacity > elementData.length > 0 ) {
grow(minCapacity);
}
}
private void grow(int minCapacity){
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if(newCapacity - minCapacity < 0){
newCapacity = minCapacity;
}
...
elementData = Arrays.copyOf(elementData,newCapacity);
}
当第一次添加元素,list.add(123);,底层就是elementData[0] = new Integer(123);
当已经添加了10个元素,达到了elementData的容量,当添加第11个元素时,底层发现容量不足,就自动扩容,int newCapacity = oldCapacity + (oldCapacity >> 1); 表示默认情况下新数组的容量为原数组容量的1.5倍。
elementData = Arrays.copyOf(elementData,newCapacity);
表示将原数组的所有元素都复制到新数组中。
2. Java的JDK8
空参构造器创建对象ArrayList list = new ArrayList();
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
对象的Object[ ] elementData = {};也就是创建一个空的Object数组。
然后添加数据,调用add()
private static final int DEFAULT_CAPACITY = 10;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
第一次add()添加元素,ensureCapacityInternal(size + 1);检查到elementData数组容量不足,扩容为长度为10的Object[ ],下次扩容和JDK7中一样,新建数组,其容量是原数组的1.5倍,然后将原数组的所有元素复制到新数组中。
Java容器:String/StringBuffer/ArrayList详解与底层扩容机制
本文详细解析了Java中String、StringBuffer和ArrayList的底层实现,重点讨论了它们如何使用数组存储数据、空参构造器的默认容量、有参构造器的容量计算以及扩容策略。了解这些有助于掌握它们的工作原理和高效使用方法。
4164

被折叠的 条评论
为什么被折叠?



