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 循环 + 下标 以及 foreach2. 迭代器是设计模式的一种
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
不适合做任意位置插入和删除比较多的场景
。