ArrayList与顺序表

1.线性表

         线性表( linear list n 个具有 相同特性的数据元素 的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列..
        线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物 理上存储时,通常以数组和链式结构的形式存储。

2.顺序表

顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成 数据的增删查改。

3. ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

说明
1. ArrayList 是以泛型方式实现的,使用时必须要先 实例化
2. ArrayList 实现了 RandomAccess 接口,表明 ArrayList 支持随机访问
3. ArrayList 实现了 Cloneable 接口,表明 ArrayList 是可以 clone的
4. ArrayList 实现了 Serializable 接口,表明 ArrayList 支持序列化
5. Vector 不同, ArrayList 不是线程安全的,在单线程下可以使用,在多线程中可以选择 Vector 或者 CopyOnWriteArrayList
6. ArrayList 底层是一段连续的空间,并且可以 动态扩容 ,是一个动态类型的顺序表

3.1 ArrayList的构造

public static void main ( String [] args ) {
// ArrayList 创建,推荐写法
// 构造一个空的列表
List < Integer > list1 = new ArrayList <> ();
// 构造一个具有 10 个容量的列表
List < Integer > list2 = new ArrayList <> ( 10 );
list2 . add ( 1 );
list2 . add ( 2 );
list2 . add ( 3 );
// list2.add("hello"); // 编译失败, List<Integer> 已经限定了, list2 中只能存储整形元
// list3 构造好之后,与 list 中的元素一致
ArrayList < Integer > list3 = new ArrayList <> ( list2 );
// 避免省略类型,否则:任意类型的元素都可以存放,使用时将是一场灾难
List list4 = new ArrayList ();
list4 . add ( "111" );
list4 . add ( 100 );
}

 

 

 3.2ArrayList的遍历

for 循环 + 下标、 foreach 、使用迭代器
public static void main ( String [] args ) {
List < Integer > list = new ArrayList <> ();
list . add ( 1 );
list . add ( 2 );
list . add ( 3 );
list . add ( 4 );
list . add ( 5 );
// 使用下标 +for 遍历
for ( int i = 0 ; i < list . size (); i ++ ) {
System . out . print ( list . get ( i ) + " " );
}
System . out . println ();
// 借助 foreach 遍历
for ( Integer integer : list ) {
System . out . print ( integer + " " );
}
System . out . println ();
//迭代器
Iterator < Integer > it = list . listIterator ();
while ( it . hasNext ()){
System . out . print ( it . next () + " " );
}
System . out . println ();
}
注意:
1. ArrayList 最长使用的遍历方式是: for 循环 + 下标 以及 foreach
2. 迭代器是设计模式的一种

3.3ArrayList的扩容机制 

每个ArrayList都有一个容量(capacity),如果容量不足,容器会自动增大底层数组的大小

        数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

源代码的扩容方式:

Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
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) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 获取旧空间大小
int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

PS:Arrays.copyOf

// 扩容数组,将新长度设置为原长度的两倍 Integer[] expandedArray = Arrays.copyOf(originalArray, originalArray.length * 2);

缺点: 

        在ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为 O(n) ,效率比较低,因此 ArrayList 不适合做任意位置插入和删除比较多的场景
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值