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()进行扩容
相关文章
|
3月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
163 4
|
3月前
|
存储 Java 索引
(Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
204 4
|
4月前
|
缓存 Java 开发者
Java 开发者必看!ArrayList 和 LinkedList 的性能厮杀:选错一次,代码慢成蜗牛
本文深入解析了 Java 中 ArrayList 和 LinkedList 的性能差异,揭示了它们在不同操作下的表现。通过对比随机访问、插入、删除等操作的效率,指出 ArrayList 在多数场景下更高效,而 LinkedList 仅在特定情况下表现优异。文章强调选择合适容器对程序性能的重要性,并提供了实用的选择法则。
274 3
|
6月前
|
存储 安全 Java
Java 学习路线 35 掌握 List 集合从入门到精通的 List 集合核心知识
本文详细解析Java中List集合的原理、常用实现类(如ArrayList、LinkedList)、核心方法及遍历方式,并结合数据去重、排序等实际应用场景,帮助开发者掌握List在不同业务场景下的高效使用,提升Java编程能力。
484 0
|
6月前
|
并行计算 Java API
Java List 集合结合 Java 17 新特性与现代开发实践的深度解析及实战指南 Java List 集合
本文深入解析Java 17中List集合的现代用法,结合函数式编程、Stream API、密封类、模式匹配等新特性,通过实操案例讲解数据处理、并行计算、响应式编程等场景下的高级应用,帮助开发者提升集合操作效率与代码质量。
296 1
|
6月前
|
Java 索引
Java ArrayList中的常见删除操作及方法详解。
通过这些方法,Java `ArrayList` 提供了灵活而强大的操作来处理元素的移除,这些方法能够满足不同场景下的需求。
641 30
|
8月前
|
人工智能 安全 JavaScript
Java ArrayList:动态数组
本文探讨Java中的数组,对比C/C++、JS/PHP/Python等语言的数组特性。文章分析了Java数组的定义、创建方式及其规范,指出其优缺点。Java数组作为引用类型,在堆上分配内存,支持动态大小,避免了C/C++中裸数组的常见问题(如越界访问)。然而,Java数组也存在性能瓶颈和设计缺陷,例如运行时的安全检查影响速度,无法创建超大数组或泛型数组,且多线程场景下缺乏同步机制。作者建议在实际开发中用集合替代数组以规避这些问题。
211 1
|
10月前
|
人工智能 Java
Java 中数组Array和列表List的转换
本文介绍了数组与列表之间的相互转换方法,主要包括三部分:1)使用`Collections.addAll()`方法将数组转为列表,适用于引用类型,效率较高;2)通过`new ArrayList&lt;&gt;()`构造器结合`Arrays.asList()`实现类似功能;3)利用JDK8的`Stream`流式计算,支持基本数据类型数组的转换。此外,还详细讲解了列表转数组的方法,如借助`Stream`实现不同类型数组间的转换,并附带代码示例与执行结果,帮助读者深入理解两种数据结构的互转技巧。
783 1
Java 中数组Array和列表List的转换
|
NoSQL Java Redis
List集合按照由小到大排序或者由大到小排序
List集合按照由小到大排序或者由大到小排序
283 0
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
621 4
Java ArrayList扩容的原理