Java顺序表解析与应用

简介: Java顺序表解析与应用

一、顺序表概念

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

二、主要功能接口实现

Java顺序表底层就是一个动态数组。其主要功能接口如下:

// 1.打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() { }
// 2.新增元素,默认在数组最后新增
public void add(int data) { }
// 3.判断当前的顺序表是不是满的
public boolean isFull() { }
// 4.在 pos 位置新增元素
public void add(int pos, int data) { }
// 5.判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 6.查找某个元素对应的位置
public int indexOf(int toFind) { return -1; }
// 7.获取 pos 位置的元素
public int get(int pos) { return -1; }
// 8.给 pos 位置的元素设为 value
public void set(int pos, int value) { }
// 9.删除第一次出现的关键字 key
public void remove(int toRemove) { }
// 10.获取顺序表长度
public int size() { return 0; }
// 11.清空顺序表
public void clear() { }
import java.util.Arrays;
public class MyArraylist {

    public int[] elem;
    // 已使用容量,初始为0
    public int usedSize;
    // 默认容量
    private static final int DEFAULT_SIZE = 5;
  // 这里采用无参的构造方法
    public MyArraylist() {
        this.elem = new int[DEFAULT_SIZE];
    }
  
    /**
     * 1.打印顺序表  
     * 注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
     * 根据usedSize判断即可
     */
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i] + " ");
        }
    }

  /**
  * 2.新增元素,默认尾插
  */ 
    public void add(int data) {
        if (isFull()) {
            resize();
        }
        this.elem[this.usedSize] = data;
        this.usedSize++;
    }
  
    // 扩容
    private void resize() {
        this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
    }

  // 检查坐标合法性
    private void checkPosInAdd(int pos) {
        if (pos < 0 || pos > this.usedSize) {
          // 这里自定义异常类
            throw new IndexException("位置不合法,请检查位置的合法性!");
        }
    }

    /**
    * 3.判断当前的顺序表是不是满的
    */
    public boolean isFull() {
        return this.usedSize == this.elem.length;
    }
  
  /**
    * 4.在 pos 位置新增元素!
    */
    public void add(int pos, int data) {
        if (isFull()) {
            resize();
        }
        checkPosInAdd(pos);
        for (int i = this.usedSize; i > pos; i--) {
            this.elem[i] = this.elem[i - 1];
        }
        this.elem[pos] = data;
        this.usedSize++;
    }

  /**
    * 5.判定是否包含某个元素
    */
    public boolean contains(int toFind) {
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elem[i] == toFind) {
                return true;
            }
        }
        return false;
    }

  /**
    * 6.查找某个元素对应的位置
    */
    public int indexOf(int toFind) {
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elem[i] == toFind) {
                return i;
            }
        }
        return -1;
    }

  /**
    * 7.获取 pos 位置的元素
    */
    public int get(int pos) {
        checkPosInAdd(pos);

        return this.elem[pos];
    }

  // 判空
    private boolean isEmpty() {
        if (this.usedSize == 0) {
            return true;
        }
        return false;
    }

  /**
    * 8.将 pos 位置的元素设为修改为: value
    */
    public void set(int pos, int value) {
        checkPosInAdd(pos);
        this.elem[pos] = value;
    }

    /**
    * 9.删除第一次出现的关键字key
    */
    public boolean remove(int key) {
        int index = indexOf(key);
        if (index == -1) {
            System.out.println("不存在关键字" + key);
            return false;
        }
        for (int j = index; j < this.usedSize - 1; j++) {
            this.elem[j] = this.elem[j + 1];
        }

        this.usedSize--;
        //注意:是在这条语句之后
        //elem[usedSize] = null;
        // 如果里面是引用类型 那么此时就需要手动置空
        elem[usedSize] = 0;
        return true;

    }

  /**
    * 10.获取顺序表长度
    */ 
    public int size() {
        return this.usedSize;
    }

  /**
    * 11.清空顺序表
    */ 
    public void clear() {
        this.usedSize = 0;
    }
}

上述过程中自定义的异常类:

public class IndexException extends RuntimeException {
  // 帮助父类构造
    IndexException() {
        super();
    }
    IndexException(String msg) {
        super(msg);
    }
}

三、Java集合框架中的 ArrayList

在 java.util 包下为我们提供了一个ArrayList类,ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表,且这里的 ArrayList 是以泛型方式实现的,使用时必须要先实例化。此外在Java中还对ArrayList有以下这些说明:(现阶段不做解释,大家可以暂时了解即可)

  1. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
  2. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
  3. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
  4. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList

1、ArrayList的构造方法

方法 解释
ArrayList() 无参构造
ArrayList(Collection<? extends E> c) 利用其他 Collection 构建 ArrayList
ArrayList(int initialCapacity) 指定顺序表初始容量

解释:

  1. 对于无参构造方法ArrayList(),在实例化ArrayList对象时不会分配内存,在add()方法中才进行内存分配。
  2. 对于ArrayList(Collection<? extends E> c),表示可以将实现了Collection接口、并且是E类型或E类型的子类的集合作为参数构造ArrayList。
  3. 相比之下ArrayList(int initialCapacity)就比较好理解了,这个构造方法是在实例化ArrayList对象时,分配指定initialCapacity大小的容量。

2、ArrayList常用方法

与此同时,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 isEmpty() 如果此列表不包含元素,则返回 true
int size() 返回此列表中的元素数
boolean contains(Object o) 判断 o 是否在线性表中
int indexOf(Object o) 返回第一个 o 所在下标
int lastIndexOf(Object o) 返回最后一个 o 的下标
List subList(int fromIndex, int toIndex) 截取部分 list

3、ArrayList 的继承关系

在《Java集合框架中》我们介绍了Java集合中的继承关系,你是否还有印象ArrayList实现了List接口:

对于List接口来说,里面涵盖了ArrayList中的方法,也就是说我们可以使用 List 接口对 ArrayList 实例进行操作,因此我们可以写出类似这种调用方式:List<Integer> list = new Arraylist<>(); 将ArrayList实例交给List接口,然后直接使用list进行相关操作。

public class OperateTest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);
        list.add(333);
        list.add(444);
        list.add(555);
        System.out.println(list);
        // 设定list2->[1,2,3] 便于测试addAll()
        List<Integer> list2 = new ArrayList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        // 末尾插入list2
        list.addAll(list2);
        System.out.println(list);

        // 获取list中有效元素个数
        System.out.println(list.size());

        // 获取和设置index位置上的元素,注意index必须介于[0, size)间
        System.out.println(list.get(0));
        list.set(0, 999);
        System.out.println(list.get(0));

        // 在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置
        list.add(1, 888);
        System.out.println(list);

        // 删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置
        // (这里如果直接写444会默认下标,需要使用引用类型)
        list.remove(new Integer(444));
        System.out.println(list);

        // 删除list中index位置上的元素,注意index不要超过list中有效元素个数,否则会抛出下标越界异常
        list.remove(list.size()-1);
        System.out.println(list);

        // 检测list中是否包含指定元素,包含返回true,否则返回false
        if(!list.contains(123456)){
            list.add(123456);
            System.out.println(list);
        }
        // 查找指定元素第一次出现的位置:indexOf从前往后找,lastIndexOf从后往前找
        System.out.println(list.indexOf(333));
        System.out.println(list.lastIndexOf(333));

        // 使用list中[0, 4)之间的元素构成一个新的SubList返回,
        // 但是和ArrayList共用一个elementData数组(对任一部分修改会影响整个部分)
        List<Integer> ret = list.subList(0, 2);
        System.out.println(ret);

        // 清空
        list.clear();
        System.out.println(list.size());
    }
}

4、ArrayList的遍历

(1) 方式一:

使用for循环+下标遍历

for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

(2) 方式二:

由于List、Arraylist集合实现了iterable接口,可以使用增强的for循环-foreach

for (Integer x:list) {
    System.out.println(x);
}

(3) 方式三:

对于Java而言只要实现了Iterable接口的集合就可以使用迭代器 iterator

Iterator<Integer> it = list.listIterator();
while(it.hasNext()){
    System.out.println(it.next());
}

总结

本篇文章主要介绍了 Java 集合中的线性结构 ArrayList ,这是一个相对比较简单,且容易理解和实现的数据结构。这里就简单总结一下顺序表 ArrayList的优缺点:

顺序表缺点:

  1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈倍数增长。假如以2倍扩容,势必会有一定的空间浪费,例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

顺序表优点:

  1. 当给定下标的时候,查找速度非常快,复杂度为O(1)
相关文章
|
8月前
|
人工智能 算法 Java
Java与AI驱动区块链:构建智能合约与去中心化AI应用
区块链技术和人工智能的融合正在开创去中心化智能应用的新纪元。本文深入探讨如何使用Java构建AI驱动的区块链应用,涵盖智能合约开发、去中心化AI模型训练与推理、数据隐私保护以及通证经济激励等核心主题。我们将完整展示从区块链基础集成、智能合约编写、AI模型上链到去中心化应用(DApp)开发的全流程,为构建下一代可信、透明的智能去中心化系统提供完整技术方案。
513 3
|
9月前
|
机器学习/深度学习 JSON Java
Java调用Python的5种实用方案:从简单到进阶的全场景解析
在机器学习与大数据融合背景下,Java与Python协同开发成为企业常见需求。本文通过真实案例解析5种主流调用方案,涵盖脚本调用到微服务架构,助力开发者根据业务场景选择最优方案,提升开发效率与系统性能。
2060 0
|
9月前
|
Java
Java的CAS机制深度解析
CAS(Compare-And-Swap)是并发编程中的原子操作,用于实现多线程环境下的无锁数据同步。它通过比较内存值与预期值,决定是否更新值,从而避免锁的使用。CAS广泛应用于Java的原子类和并发包中,如AtomicInteger和ConcurrentHashMap,提升了并发性能。尽管CAS具有高性能、无死锁等优点,但也存在ABA问题、循环开销大及仅支持单变量原子操作等缺点。合理使用CAS,结合实际场景选择同步机制,能有效提升程序性能。
|
9月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
605 100
|
10月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
8月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
703 8
|
8月前
|
存储 安全 Java
《数据之美》:Java集合框架全景解析
Java集合框架是数据管理的核心工具,涵盖List、Set、Map等体系,提供丰富接口与实现类,支持高效的数据操作与算法处理。
|
9月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
1460 12
|
9月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
9月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
1475 1

推荐镜像

更多
  • DNS