标签:index 元素 Java int ArrayList elementData 基础 size
Java基础之ArrayList
1. 概述
ArrayList实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量(capacity),表示底层数组的实际大小,容器内存储元素的个数不能多于当前容量。当向容器中添加元素时,如果容量不足,容器会自动增大底层数组的大小。前面已经提过,Java泛型只是编译器提供的语法糖,所以这里的数组是一个Object数组,以便能够容纳任何类型的对象。
size(), isEmpty(), get(), set()方法均能在常数时间内完成,add()方法的时间开销跟插入位置有关,addAll()方法的时间开销跟添加元素的个数成正比。其余方法大都是线性时间。
为追求效率,ArrayList没有实现同步(synchronized),如果需要多个线程并发访问,用户可以手动同步,也可使用Vector替代。
- 会自动扩容的数组,线程不安全
- 查询快,增删慢
2. 底层实现
- Object数组实现,存入元素时会丢失类型
// 默认容量
private static final int DEFAULT_CAPACITY = 10;
// 底层存储数组
transient Object[] elementData;
- 新创建的ArrayList,若在创建时没有指定初始容量,则默认初始容量为0,当添加第一个元素时,扩展容量为默认容量10 ( ArrayList的初始容量现在为0,不再是10了)
- 当容量不为0时,若不够用,则扩展为原长度的1.5倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 右移相当于除以2,整体相当于扩容1.5倍
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);
}
3. 常用方法介绍
3.1 set()
- 作用:设置给定下标处的值,并返回坐标处的旧值
- 入参:下标,值
- 返回值:给定坐标处的旧值
public E set(int index, E element) {
rangeCheck(index);//下标越界检查
E oldValue = elementData(index);
elementData[index] = element;//赋值到指定位置,复制的仅仅是引用
return oldValue;
}
注意:入参中的下标必须小于数组长度,否则会报 IndexOutOfBoundsException
3.2 get()
- 作用:获取特定下标处的值
- 入参:下标
- 返回值:给定坐标处的值(由于底层数组是Object[],得到元素后需要进行类型转换)
public E get(int index) {
rangeCheck(index);
return (E) elementData[index];//注意类型转换
}
3.3 add()
- 作用:往尾部(特定下标处)添加元素
- 入参:(下标)元素
- 返回值:true
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 空间检查
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index); // 下标检查
ensureCapacityInternal(size + 1); // 空间检查
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
3.4 addAll()
addAll()
方法能够一次添加多个元素,根据位置不同也有两个版本,一个是在末尾添加的 addAll(Collection<? extends E> c)
方法,一个是从指定位置开始插入的 addAll(int index, Collection<? extends E> c)
方法。跟 add()
方法类似,在插入之前也需要进行空间检查,如果需要则自动扩容;如果从指定位置插入,也会存在移动元素的情况。 addAll()
的时间复杂度不仅跟插入元素的多少有关,也跟插入的位置相关。
3.5 remove()
remove()
方法也有两个版本,一个是 remove(int index)
删除指定位置的元素,另一个是 remove(Object o)
删除第一个满足o.equals(elementData[index])
的元素。删除操作是 add()
操作的逆过程,需要将删除点之后的元素向前移动一个位置。需要注意的是为了让GC起作用,必须显式的为最后一个位置赋null值。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; //清除该位置的引用,让GC起作用
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
注意:如果ArrayList中的元素类型时Integer,则用remove方法参数还是被解释成下标!
4. 遍历方法
4.1 Iterator方法遍历
for(Iterator it = list.iterator();it.hasNext();){
System.out.println(it.next());
}
遍历时不能使用list的删除方法,否则会报并发修改异常,但是可以使用迭代器的删除方法
4.2 for循环遍历
for(int i = 0;i < list.size(); i ++){
System.out.println(list.get(i));
}
遍历时可以修改list
4.3 增强for循环遍历
for(String tmp:list){
System.out.println(tmp);
}
底层基于迭代器实现,遍历时不能使用list的删除方法,否则会报并发修改异常,但是可以使用迭代器的删除方法
5. 类比LinkedList、Vector
List主要有ArrayList、LinkedList与Vector几种实现。
这三者都实现了List 接口,使用方式也很相似,主要区别在于因为实现方式的不同,所以对不同的操作具有不同的效率。
ArrayList 是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.
LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.
当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.
Vector 和ArrayList类似,但属于强同步类。如果你的程序本身是线程安全的(thread-safe,没有在多个线程之间共享同一个集合/对象),那么使用ArrayList是更好的选择。
Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.
而 LinkedList 还实现了 Queue 接口,该接口比List提供了更多的方法,包括 offer(),peek(),poll()等.
注意: 默认情况下ArrayList的初始容量非常小,所以如果可以预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。
6. 参考
标签:index,元素,Java,int,ArrayList,elementData,基础,size 来源: https://blog.csdn.net/chuangshangbeidong/article/details/120647253
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。