**1.**线性表
线性表( linear list ) 是 n 个具有 相同特性的数据元素 的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列..
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物 理上存储时,通常以数组和链式结构的形式存储。
**2.**顺序表
顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成 数据的增删查改。
3. ArrayList****简介
在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
【 说明 】
- ArrayList 是以泛型方式实现的,使用时必须要先 实例化
- ArrayList 实现了 RandomAccess 接口,表明 ArrayList 支持随机访问
- ArrayList 实现了 Cloneable 接口,表明 ArrayList 是可以 clone的
- ArrayList 实现了 Serializable 接口,表明 ArrayList 是 支持序列化 的
- 和 Vector 不同, ArrayList 不是线程安全的,在单线程下可以使用,在多线程中可以选择 Vector 或者 CopyOnWriteArrayList
- 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 ();
}
注意:
- ArrayList 最长使用的遍历方式是: for 循环 + 下标 以及 foreach
- 迭代器是设计模式的一种
3.3ArrayList****的扩容机制
每个ArrayList都有一个容量(capacity),如果容量不足,容器会自动增大底层数组的大小
数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。
源代码的扩容方式:
java
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 不适合做任意位置插入和删除比较多的场景 。