作者 :whispar
专栏 :小题练手刷题,面试,求职,快来牛客网一起成为offer收割机!
点击注册收割offer
目录
一、线性表
二、 ArrayLIst简介
三、模拟实现ArrayList
四、ArrayList中常用方法
五、ArrayList的扩容机制
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改 。
【说明】
ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList
ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
在集合框架中,ArrayList是一个普通的类,实现了List接口
通过查看ArrayList的源码,我们可以得知ArrayList的底层是由数组来实现的,并初始化了默认的大小,并且实现了Cloneable和RandomAccess 等许多常用接口
2,ArrayList构造方法
无参的构造方法
使用默认的size为10的空数组,在构造方法中没有对数组长度进行设置,会在后续调用add方法的时候进行扩容,里面是一个赋值操作,右边是一个空容量数组,左边则是存储数据的容器,以下是参照源码分析;
//默认空容量数组,长度为0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//集合真正存储数据的容器
transient Object[] elementData;
指定初始化容量的构造方法
参数大于0,elementData初始化为initialCapacity大小的数组;参数等于0,elementData初始化为空数组;参数小于0,抛出异常;
参数为Collection类型的构造方法
将一个参数为Collection的集合转变为ArrayList(实际上就是将集合中的元素换为了数组的形式);如果传入的集合为null会抛出空指针异常,只要传入的参数为E或者为E的子类,都可以传入。
使用
public class TestArraylist {
public static void main(String[] args) {
/**
* 一、Arraylist的构造和初始化
*/
//1.直接
List list = new ArrayList<>();
//2.带参数的构造
List list2 = new ArrayList<>(5);
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);
list2.add(4);
list2.add(6);
list2.add(8);
list2.add(890);
System.out.println(list2);
//3.利用其他 Collection 的子类 构建 ArrayList
//(1)使用list2
List list3 = new ArrayList<>(list2);
list3.add(34);
System.out.println(list3.size());
System.out.println(list3);
//(2)使用Linklist构建
LinkedList linkedList = new LinkedList<>();
linkedList.add(3);
linkedList.add(9);
linkedList.add(9);
linkedList.add(4);
linkedList.add(4);
List list4 = new ArrayList<>(linkedList);
System.out.println(list4);
}
}
我们可以使用数组来简单实现ArrayList
初始化
class MyArraylist extends PosWronglyException {
public int[] elem;
public int usedSize;
//默认容量
private static final int DEFAULT_SIZE = 10;
public MyArraylist() {
this.elem = new int[DEFAULT_SIZE];
}
打印顺序表
/**
* 打印顺序表:
* 根据usedSize判断即可
*/
public void display() {
for (int i = 0; i < usedSize; i++) {
System.out.println(this.elem[i]);
}
System.out.println();
}
新增元素
// 新增元素,默认在数组最后新增
public void add(int data) {
if (isFull()) {
System.out.println("顺序表此时为满");
this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
}
this.elem[usedSize] = data;
this.usedSize++;
}
/**
* 在 pos 位置新增元素
* 如果pos下标不合法,那么就会抛出一个 PosWrongfulException
*/
// 在 pos 位置新增元素
public void add(int pos, int data) throws PosWronglyException {
//1、判断顺序表是否为满
if (isFull()) {
System.out.println("顺序表已满");
this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
}
//2、判断pos位置是否合理
if (pos < 0 || pos > this.usedSize) {
System.out.println("pos 位置不合法");
throw new PosWronglyException("pos 位置不合法");
}
//3、合理,则开始从后向前挪动元素
for (int i = usedSize - 1; i >= pos; i--) {
this.elem[i + 1] = this.elem[i];
}
//4、填入元素
this.elem[pos] = data;
//5.usedsize增加一个
this.usedSize++;
}
判满判空
/**
* 判断当前的顺序表是不是满的!
*
* @return true:满 false代表空
*/
public boolean isFull() {
if (size() >= this.elem.length) {
return true;
}
return false;
}
//判断顺序表是否为空
public boolean isEmpty() {
return size() == 0;
}
删除元素
/**
* 删除第一次出现的关键字key
*
* @param key
*/
public void remove(int key) {
if (isEmpty()) {
System.out.println("当前顺序表为空");
throw new EmptyException("顺序表 为空");
}
int index = this.indexOf(key);
if(index == -1){
System.out.println(" 顺序表中没有这个数字");
return;
}
for(int i = index ;i < usedSize -1;i++){
this.elem[i] = this.elem[i+1];
}
this.usedSize--;
}
查找与获取元素
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.size(); i++) {
if (this.elem[i] == toFind)
return i;
}
return -1;
}
// 获取 pos 位置的元素
public int get(int pos) {
if (isEmpty()) {
System.out.println("当前顺序表为空");
throw new EmptyException("顺序表 为空");
}
if (pos < 0 || pos > this.usedSize) {
System.out.println("pos 位置不合法");
throw new PosWronglyException("pos 位置不合法");
}
return this.elem[pos];
}
测试
public class TestList {
public static void main(String[] args) {
MyArraylist myArraylist = new MyArraylist();
myArraylist.add(10);
myArraylist.add(20);
myArraylist.add(30);
try{
myArraylist.add(3,100);
}catch (PosWronglyException e){
e.printStackTrace();
}
myArraylist.display();
System.out.println("++++++++++++++++++");
System.out.println(myArraylist.isEmpty());
System.out.println(myArraylist.isFull());
System.out.println(myArraylist.contains(10));
myArraylist.remove(10);
myArraylist.display();
try{
System.out.println(myArraylist.get(1));
}catch(PosWronglyException e){
e.printStackTrace();
}
}
}
方法 | 解释 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List |
ArrayList的遍历
ArrayLsit的遍历包含三种,for 循环遍历、for each()遍历,使用迭代器进行遍历
/**
* Arraylist中的遍历
*/
//1、for循环遍历
for (int i = 0; i < list4.size() ; i++) {
System.out.print(list4.get(i)+" ");
}
System.out.println();
//2、for each()进行遍历
for(Integer integer:list4){
System.out.print(integer+" ");
}
System.out.println();
for (int x:list4) {
System.out.print(x+" ");
}
System.out.println();
//3、使用迭代器进行遍历
Iterator it =list4.iterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
System.out.println();
扩容的核心源代码:
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);
}
总结:
扩容的大小是原先数组的1.5倍;
若值
newCapacity
比传入值minCapacity
还要小,则使用传入minCapacity
,若newCapacity
比设定的最大容量大,则使用最大整数值;ArrayList底层是数组实现的,那么每次添加数据时会不断地扩容,这样的话会占内存,性能低,所以导致时间很长,我们可以用ArrayList的指定初始化容量的构造方法来解决性能低的问题。
使用copyOf()进行扩容