Java顺序表解析与应用

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 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面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
28 2
|
11天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
64 6
|
11天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
3天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
4天前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
RS-485网络中的标准端接与交流电端接应用解析
RS-485,作为一种广泛应用的差分信号传输标准,因其传输距离远、抗干扰能力强、支持多点通讯等优点,在工业自动化、智能建筑、交通运输等领域得到了广泛应用。在构建RS-485网络时,端接技术扮演着至关重要的角色,它直接影响到网络的信号完整性、稳定性和通信质量。
|
9天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
21 4
|
8天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
8天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
16 0
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
68 0

推荐镜像

更多
下一篇
无影云桌面