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 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
269 100
|
3月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
303 101
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
2月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
108 7
|
2月前
|
存储 Java 索引
(Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
183 4
|
4月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
295 23
|
3月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
|
4月前
|
安全 Java 开发者
Java集合框架:详解Deque接口的栈操作方法全集
理解和掌握这些方法对于实现像浏览器后退功能这样的栈操作来说至关重要,它们能够帮助开发者编写既高效又稳定的应用程序。此外,在多线程环境中想保证线程安全,可以考虑使用ConcurrentLinkedDeque,它是Deque的线程安全版本,尽管它并未直接实现栈操作的方法,但是Deque的接口方法可以相对应地使用。
275 12
|
4月前
|
存储 缓存 安全
Java集合框架(三):Map体系与ConcurrentHashMap
本文深入解析Java中Map接口体系及其实现类,包括HashMap、ConcurrentHashMap等的工作原理与线程安全机制。内容涵盖哈希冲突解决、扩容策略、并发优化,以及不同Map实现的适用场景,助你掌握高并发编程核心技巧。
|
4月前
|
存储 NoSQL Java
Java Stream API:集合操作与并行处理
Stream API 是 Java 8 提供的集合处理工具,通过声明式编程简化数据操作。它支持链式调用、延迟执行和并行处理,能够高效实现过滤、转换、聚合等操作,提升代码可读性和性能。

热门文章

最新文章

  • 1
    PHP 数组查找:为什么 `isset()` 比 `in_array()` 快得多?
    173
  • 2
    Java 中数组Array和列表List的转换
    653
  • 3
    JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
    589
  • 4
    通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式、聚合统计、处理树结构数据和性能优化,reduce()的使用详解(附实际应用代码)
    1338
  • 5
    通过array.some()实现权限检查、表单验证、库存管理、内容审查和数据处理;js数组元素检查的方法,some()的使用详解,array.some与array.every的区别(附实际应用代码)
    430
  • 6
    通过array.every()实现数据验证、权限检查和一致性检查;js数组元素检查的方法,every()的使用详解,array.some与array.every的区别(附实际应用代码)
    260
  • 7
    多维数组操作,不要再用遍历循环foreach了!来试试数组展平的小妙招!array.flat()用法与array.flatMap() 用法及二者差异详解
    172
  • 8
    别再用双层遍历循环来做新旧数组对比,寻找新增元素了!使用array.includes和Set来提升代码可读性
    201
  • 9
    Array.forEach实战详解:简化循环与增强代码可读性;Array.forEach怎么用;面对大量数据时怎么提高Array.forEach的性能
    127
  • 10
    深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解
    504