Java集合-- Array List 与顺序表

简介: Array List 与顺序表详解

ArrayList与顺序表


一、线性表

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

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

二、 ArrayLIst简介

img

说明

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

在集合框架中,ArrayList是一个普通的类,实现了List接口

image-20221005165335074

通过查看ArrayList的源码,我们可以得知ArrayList的底层是由数组来实现的,并初始化了默认的大小,并且实现了Cloneable和RandomAccess 等许多常用接口

2,ArrayList构造方法

  • 无参的构造方法

image-20221005165946090

使用默认的size为10的空数组,在构造方法中没有对数组长度进行设置,会在后续调用add方法的时候进行扩容,里面是一个赋值操作,右边是一个空容量数组,左边则是存储数据的容器,以下是参照源码分析;

//默认空容量数组,长度为0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//集合真正存储数据的容器
transient Object[] elementData; 
  • 指定初始化容量的构造方法

image-20221005170110532

参数大于0,elementData初始化为initialCapacity大小的数组;参数等于0,elementData初始化为空数组;参数小于0,抛出异常;

  • 参数为Collection类型的构造方法
    image-20221005170219597

将一个参数为Collection的集合转变为ArrayList(实际上就是将集合中的元素换为了数组的形式);如果传入的集合为null会抛出空指针异常,只要传入的参数为E或者为E的子类,都可以传入。

  • 使用
public class TestArraylist {
    public static void main(String[] args) {
        /**
         * 一、Arraylist的构造和初始化
         */
        //1.直接
        List<Integer> list = new ArrayList<>();
        //2.带参数的构造
        List<Integer> 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<Integer> list3 = new ArrayList<>(list2);
        list3.add(34);
        System.out.println(list3.size());
        System.out.println(list3);
        //(2)使用Linklist构建
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(3);
        linkedList.add(9);
        linkedList.add(9);
        linkedList.add(4);
        linkedList.add(4);
        List<Integer> list4 = new ArrayList<>(linkedList);
        System.out.println(list4);
        }
}

三、模拟实现ArrayList

我们可以使用数组来简单实现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();
        }

    }
}

image-20221005171616843

四、ArrayList中常用方法

方法 解释
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 subList(int fromIndex, int toIndex)
  • 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();

五、ArrayList的扩容机制

扩容的核心源代码:

image-20221005172624944

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. 扩容的大小是原先数组的1.5倍;
  2. 若值newCapacity比传入值minCapacity还要小,则使用传入minCapacity,若newCapacity比设定的最大容量大,则使用最大整数值;
  3. ArrayList底层是数组实现的,那么每次添加数据时会不断地扩容,这样的话会占内存,性能低,所以导致时间很长,我们可以用ArrayList的指定初始化容量的构造方法来解决性能低的问题。
  4. 使用copyOf()进行扩容
相关文章
|
4月前
|
NoSQL Java Redis
List集合按照由小到大排序或者由大到小排序
List集合按照由小到大排序或者由大到小排序
69 0
|
4月前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
103 4
Java ArrayList扩容的原理
|
4月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
5月前
|
Java 程序员
Java|List.subList 踩坑小记
不应该仅凭印象和猜测,就开始使用一个方法,至少花一分钟认真读完它的官方注释文档。
58 1
|
5月前
|
Java 程序员 编译器
Java|如何正确地在遍历 List 时删除元素
从源码分析如何正确地在遍历 List 时删除元素。为什么有的写法会导致异常,而另一些不会。
108 3
|
5月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
110 5
|
5月前
|
安全 Java 程序员
Java集合之战:ArrayList vs LinkedList,谁才是你的最佳选择?
本文介绍了 Java 中常用的两个集合类 ArrayList 和 LinkedList,分析了它们的底层实现、特点及适用场景。ArrayList 基于数组,适合频繁查询;LinkedList 基于链表,适合频繁增删。文章还讨论了如何实现线程安全,推荐使用 CopyOnWriteArrayList 来提升性能。希望帮助读者选择合适的数据结构,写出更高效的代码。
205 3
|
5月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
51 3
|
5月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
85 0
|
7月前
|
存储 Java 索引
Java 中 ArrayList 和 LinkedList 之间的区别
【8月更文挑战第22天】
168 1

热门文章

最新文章