Java 动态数组的实现示例

目录
  • 静态数组
  • 动态数组的实现原理
    • 1.添加元素
    • 2.删除元素
    • 3.数组扩容
    • 4.数组缩减

静态数组

Java中最基本的数组大家肯定不会陌生:

int[] array = new int[6];
for (int i = 0; i < array.length; i++){
    array[i] = 2 * i + 1;
}

通过循环把元素放入指定的位置中,类似于这样:

这是一个静态数组,因为我们在第一步初始化的时候就已经固定了它的长度,后面再也无法改变。所以,由于有这个限制,静态数组不适用于那些不确定储存多少数据的场景。
但是如果数组满了,能否再新建一个更长一些的数组,把原数组这些元素再转移到新数组中呢?这样一来,数组就可以继续使用了。按照这个思路,我们就可以创建基于静态数组的动态数组。

动态数组的实现原理

“动态”主要体现在以下几方面:

1.添加元素

不局限于只在数组末尾添加,而是能够随意选择索引位置(只要不超过数组长度)。例如在索引为1处添加元素4:

从图中可以看出,需要将index处及右侧的元素依次向右移动一个单位(从末位元素开始),最后用新增元素覆盖index处元素。

2.删除元素

同添加元素,也可根据索引进行选择。例如删除索引为0处的元素3:

删除元素移动元素的方向与添加元素正好相反,从index处开始,直接使用后一位元素覆盖前一位元素,最后将末位元素置为null。

3.数组扩容

数组一旦装满元素,可触发数组扩容,即新建一个更长的数组,将原数组元素转移到新数组中,并将引用指向新数组,完成数组的变更;

4.数组缩减

如果数组元素相对总容量来说过少(例如数组元素个数小于数组容量的1/4),便可触发数组缩减,即新建一个更短的数组,并转移元素至新数组。

代码实现

以下通过新建一个 Array 类,依次实现这几个重要功能:

public class Array<E> {
    private E[] data;       // 使用静态数组存放数组元素
    private int size;       // 记录数组元素数量

    public Array(int capacity) {
        this.data = (E[]) new Object[capacity];
        this.size = 0;
    }

    public Array() {
        this(10);   // 默认capacity为10
    }

    // 数组扩容/缩减
    public void resize(int newCapacity) {
        // 新数组长度必须大于0
        if (newCapacity < 0) throw new IllegalArgumentException("capacity must > 0!");
        // 创建新数组
        E[] newData = (E[]) new Object[newCapacity];
        // 将原数组元素放入新数组中
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        // 将引用指向新数组
        data = newData;
    }

    /**
     * 在指定位置添加元素
     * 指定位置处的元素需要向右侧移动一个单位
     * @param index   索引
     * @param element 要添加的元素
     */
    public void add(int index, E element) {
        if (index < 0 || index > size) throw new IllegalArgumentException("Illegal index, index must > 0 and <= size!");
        // 数组满员触发扩容
        if (size == data.length) {
            resize(2 * data.length);  // 扩容为原数组的2倍
        }
        // 从尾部开始,向右移动元素,直到index
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        // 添加元素
        data[index] = element;
        size++;
    }

    // 数组头部添加元素
    public void addFirst(E element) {
        add(0, element);
    }

    // 数组尾部添加元素
    public void addLast(E element) {
        add(size, element);
    }

    /**
     * 删除指定位置元素
     * 通过向左移动一位,覆盖指定位置处的元素,实现删除元素(data[size - 1] = null)
     * @param index 索引
     */
    public E remove(int index) {
        if (index < 0 || index > size) throw new IllegalArgumentException("Illegal index, index must > 0 and < size!");
        // 数组长度为0时抛出异常
        if (size == 0) throw new IllegalArgumentException("Empty array!");
        E removedElement = data[index];
        // 向左移动元素
        for (int i = index; i < size - 1; i++) {
            data[i] = data[i + 1];
        }
        // 将尾部空闲出的位置置为空,释放资源
        data[size - 1] = null;
        size--;
        // size过小触发数组缩减
        if (size == data.length / 4 && data.length / 2 != 0) resize(data.length / 2);
        return removedElement;
    }

    // 删除头部元素
    public E removeFirst() {
        return remove(0);
    }

    // 删除尾部元素
    public E removeLast() {
        return remove(size - 1);
    }

    // 重写Override方法,自定义数组显示格式
    @Override
    public String toString() {
        StringBuilder str = new StringBuilder();
        // 显示数组的整体情况(长度、总容量)
        str.append(String.format("Array: size = %d, capacity = %d\n[", size, data.length));
        // 循环添加数组元素至str
        for (int i = 0; i < size; i++) {
            str.append(data[i]);
            if (i < size - 1) str.append(", ");
        }
        str.append("]");
        return str.toString();
    }
}

接下来我们测试一下这个数组的使用情况:

public static void main(String[] args) {
        // 添加10个元素
        Array<Integer> arr = new Array<>();
        for (int i = 0; i < 10; i++)
            arr.add(i, i);
        // 查看数组当前状态
        System.out.println(arr);
        // 继续添加元素,观察是否扩容
        arr.add(arr.size, 7);
        System.out.println(arr);

        // 再删除6个元素,观察是否缩减
        for (int i = 0; i < 6; i++) {
            System.out.println("元素" + arr.removeFirst() + "已被删除!");
        }
        System.out.println(arr);
    }

/*
输出结果:
Array: size = 10, capacity = 10
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array: size = 11, capacity = 20
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 7]
元素0已被删除!
元素1已被删除!
元素2已被删除!
元素3已被删除!
元素4已被删除!
元素5已被删除!
Array: size = 5, capacity = 10
[6, 7, 8, 9, 7]
*/

可以看到,当数组满员后,继续添加元素可以成功触发数组扩容;而当数组元素过少时,也会触发缩减。
再实现几个常用方法来完善我们的动态数组类:

    // 获取数组长度
    public int getSize() {
        return size;
    }

    // 获取数组总容量
    public int getCapacity() {
        return data.length;
    }

    // 判断数组是否为空
    public boolean isEmpty() {
        return getSize() == 0;
    }

    // 查找指定元素在数组中的位置
    public int search(E element) {
        for (int i = 0; i < getSize(); i++) {
            if (data[i].equals(element)) {
                return i;
            }
        }
        // -1表示未找到
        return -1;
    }

    // 判断指定元素是否在数组中
    public boolean contains(E element) {
        return search(element) != -1;
    }

    // 按照索引查找元素值
    public E get(int index) {
        if (index < 0 || index > size) throw new IllegalArgumentException("Illegal index, index must > 0 and < size!");
        return data[index];
    }

    // 查找头部元素
    public E getFirst() {
        return get(0);
    }

    // 查找尾部元素
    public E getLast() {
        return get(getSize() - 1);
    }

    // 设置指定位置的元素值
    public void set(int index, E element) {
        if (index < 0 || index > size) throw new IllegalArgumentException("Illegal index, index must > 0 and < size!");
        data[index] = element;
    }

    /**
     * 按照元素值删除
     * 只删除数组中第一个元素值与指定值相等的元素
     * @param element 指定元素值
     */
    public boolean removeElement(E element) {
        int index = search(element);
        if (index != -1) {
            remove(index);
            return true;
        }
        return false;
    }

    /**
     * 按照元素值删除
     * 删除数组中所有值与指定值相等的元素
     *
     * @param element 指定元素值
     */
    public boolean removeElementAll(E element) {
        boolean isRemoved = false;
        int i = getSize() - 1;
        while (i >= 0) {
            if (data[i].equals(element)) {
                remove(i);
                isRemoved = true;
            }
            i--;
        }
        return isRemoved;
    }

从外部调用者的角度,无法觉察到其中的数组变更操作,感觉就是一个动态数组,但是由于扩容和缩减操作均需要新建数组,并且遍历原数组,会导致过多的开销,所以从性能上来说,并不是好的解决方案。后面我们将学习更加高效的数据结构。

到此这篇关于Java 动态数组的实现示例的文章就介绍到这了,更多相关Java 动态数组内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java版C语言版简单使用静态语言实现动态数组的方法

    动态语言相对于静态语言的一个优势,就是数组可以不需要预先确定大小,对于一些数组长度不确定的场景下是非常有用的.像PHP,只需要声明一下数组 $arr = array() 然后就可以直接 $arr[] = 1,$arr[] = 2,$arr[] = 3...这样一直加元素了,删除一个元素就直接使用unset($arr[1]),元素的空间就被释放了,而C和JAVA原生的数组就没有这么方便,声明的时候就必须先预先确定长度,由编译器分配相应的内存空间.不过通过一些巧妙的做法,也是可以实现一样的功能的,这

  • Java二维数组与动态数组ArrayList类详解

    Java二维数组 Java 语言中提供的数组是用来存储固定大小的同类型元素. 1.二维数组初始化和声明 数组变量的声明,和创建数组可以用一条语句完成,如下所示: int a[][] = new int[2][3]; int[][] arr = {{1,2,3},{4,5,6},{7,8,9}}; 2.二维数组遍历 //遍历二维数组 public class Traverse_a_two_dimensional_array { public static void main(String[] ar

  • Java封装数组之动态数组实现方法详解

    本文实例讲述了Java封装数组之动态数组实现方法.分享给大家供大家参考,具体如下: 前言:在此之前,我们封装的数组属于静态数组,也即数组空间固定长度,对于固定长度的数组当元素超过容量时会报数组空间不足.为了能更好的使用数组,我们来实现一个可以自动扩充容量的数组. 实现思路: 1.当数组容量达到事先定义值时创建一个空间是data数组两倍的newData数组(扩容): 2.把data数组中的元素全部赋值到newData数组中: 3.把data数组重新执行newData数组. 一.定义核心扩容方法 /

  • java实现动态数组

    本文实例为大家分享了java实现动态数组的具体代码,供大家参考,具体内容如下 数组最大的优点︰快速查询.scores[2].数组最好应用于"索引有语意"的情况,但是如果索引比较长就还是不要用数组了,比如身份证号,太长了. Java提供给我们的数组是静态数组,大小在一开始就定下来了,所以我们要创建一个动态数组,来满足我们的需求.其实原理挺简单,初次创建的时候赋予一个初始大小,当容量不够用时进行扩容,下列代码最关键的是resize方法 public class Array<E>

  • Java 自定义动态数组方式

    Java自定义动态数组 1.静态数组向动态数组转变 (1)静态数组,数组空间固定长度 这个数组空间总长为4,如果此时新插入一个数据就会报数组空间不足 (2)静态数组如何转变成动态数组 第一步:创建一个空间是data数组两倍的newData数组(扩容): 第二步:把data数组中的元素全部赋值到newData数组: 2.数组扩容程序 // 数组扩容 private void resize(int newCapacity){ E[] newData = (E[]) new Object[newCap

  • Java动态数组添加数据的方法与应用示例

    本文实例讲述了Java动态数组添加数据的方法与应用.分享给大家供大家参考,具体如下: 输入客户的姓名,客户的人数不定.待输入完成后,请打印出客户的名单,并定义一个方法查询客户是否在这些客户中. 代码示例: package com.jredu.ch06.exer; import java.util.Arrays; import java.util.Scanner; public class CustomBiz { public String[] custom; public void addNam

  • Java 动态数组的实现示例

    目录 静态数组 动态数组的实现原理 1.添加元素 2.删除元素 3.数组扩容 4.数组缩减 静态数组 Java中最基本的数组大家肯定不会陌生: int[] array = new int[6]; for (int i = 0; i < array.length; i++){ array[i] = 2 * i + 1; } 通过循环把元素放入指定的位置中,类似于这样: 这是一个静态数组,因为我们在第一步初始化的时候就已经固定了它的长度,后面再也无法改变.所以,由于有这个限制,静态数组不适用于那些不

  • Java动态编译执行代码示例

    在某些情况下,我们需要动态生成java代码,通过动态编译,然后执行代码.JAVAAPI提供了相应的工具(JavaCompiler)来实现动态编译.下面我们通过一个简单的例子介绍,如何通过JavaCompiler实现java代码动态编译. 一.获取JavaCompiler JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 获取JDK提供的java编译器,如果没有提供编译器,则返回null: 二.编译 //获取java文件管理

  • Java动态数组Arraylist存放自定义数据类型方式

    目录 Java动态数组Arraylist存放自定义数据类型 自定义一个动态数组ArrayList,加深对动态数组的理解 Java动态数组Arraylist存放自定义数据类型 class Point { int x; int y; public Point(int x,int y) { this.x=x; this.y=y; } } public class Test { public static void main(String[] args) { // TODO Auto-generated

  • Java动态加载类示例详解

    在讲解动态加载类之前呢,我们先弄清楚为什么要动态加载类,静态加载不行吗?我们可以看下面的实例: 我在文件夹里写了Office.java 类和 Word.java类,如下: Office.java class Office{ public static void main(String[] args){ if(args[0].equals("Word")){ Word w = new Word(); w.start(); } if(args[0].equals("Excel&q

  • Java ArrayList集合详解(Java动态数组)

    目录 一.ArrayList集合的概述和基本使用 1.概述 2.基本使用 二.ArrayList集合的详细介绍 1.定义一个ArrayList集合 2.ArrayList集合常用的方法 3.将"类"存入ArrayList集合 4.遍历ArrayList集合 5.将基本数据类型存入ArrayList集合 6.ArrayList作为方法的参数 7.ArrayList作为方法的返回值 一.ArrayList集合的概述和基本使用 1.概述 ArrayList是集合的一种实现,Collectio

  • Java用数组实现循环队列的示例

    复习了下数据结构,用Java的数组实现一下循环队列. 队列的类 //循环队列 class CirQueue{ private int QueueSize; private int front; private int rear; private int[] queueList ; public CirQueue(int QueueSize){ this.QueueSize = QueueSize; queueList = new int[QueueSize]; front = 0; rear =

  • java 动态增加定时任务示例

    整理文档,java 动态增加定时任务示例,直接上代码. import org.apache.tools.ant.util.DateUtils; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; import ja

  • JavaScript动态创建二维数组的方法示例

    本文实例讲述了JavaScript动态创建二维数组的方法.分享给大家供大家参考,具体如下: 学过C语言的我太耿直 一般这种情况下我会直接 var arr = new Array[10][10]; 但是不出意外的话这样是会报错的,因为在js中根本没有这样的语法 在这之前,让我们先来回顾一下js中是怎么样创建一维数组的: 使用数组直接量,这个是最简单的,在方括号内将数组元素用逗号隔开即可: var arr = [ ]; //空数组 var s = [1,2,3,4]; //4个元素的数组 var n

随机推荐