当前位置: 首页 > news >正文

java基础 数组Array的介绍

Array

  • 数组定义
  • 一维数组
  • 多维数组
  • 动态数组
  • 常见方法
    • Arrays
      • 排序
        • 1.sort() 排序
        • 2.parallelSort() 排序
      • 查找:binarySearch()
      • 填充:fill()
      • 比较:equals() 和 deepEquals()
      • 复制:copyOf() 和 copyOfRange()
      • 转换为列表:asList()
      • 转换为字符串:toString() 和 deepToString()
      • 流操作:stream()
      • Arrays总结
    • ArrayUtils

数组定义

数组(Array)是一种线性数据结构,用于存储相同类型元素的集合。元素通过索引(下标)访问,索引通常从 0 开始

一维数组

(1) 初始化
静态初始化:直接指定元素值。

int[] arr = {1, 2, 3}; // 这两种一样 可以简化new int[]
int[] arr = new int[]{1, 2, 3}; 

动态初始化:指定长度,填充默认值(如 0、null)。

int[] arr = new int[5]; // Java

两者区别主要在于元素是否确定下来,但初始容量的大小已经固定了

(2) 访问元素
元素通过**索引(下标)**访问获取操作,如 int[] is = { 5, 8, 3, 12, 24, 30, 18, 1, 10, 13 };
数组的位置这index起始为0 对应数组元素
在这里插入图片描述
代码示例如下

int value = arr[0]; // 获取第一个元素
arr[1] = 10;        // 修改第二个元素

(3) 遍历数组

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]); // 按索引遍历
}

for (int num : arr) {
    System.out.println(num);    // 增强for循环(Java)
}

(4) 插入元素
尾部插入:直接赋值(若空间足够)。
中间插入:需将后续元素后移。

// 在索引2处插入元素99
for (int i = arr.length-1; i > 2; i--) {
    arr[i] = arr[i-1]; // 后移元素
}
arr[2] = 99;

(5) 删除元素
中间删除:需将后续元素前移。

// 删除索引2处的元素
for (int i = 2; i < arr.length-1; i++) {
    arr[i] = arr[i+1]; // 前移元素
}
arr[arr.length-1] = 0; // 末尾置空(可选)

多维数组

数组的元素也可以是数组,形成多维结构(如二维数组表示矩阵):

// Java二维数组
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6}
};
System.out.println(matrix[1][2]); // 输出6

这种方式常见的是在 IO流的拷贝操作中

    public void copyTest02(){
        try(FileReader fileReader = new FileReader("fileWriteTest.txt");
            FileWriter fileWriter = new FileWriter("D:\\duxx\\fileWriteTestCopy.txt")){
            char[] chars = new char[1024*1024];// 2MB
            int readLen;
            while ((readLen = fileReader.read(chars)) != -1){
                fileWriter.write(chars,0,readLen);
            }
            fileWriter.flush();
            System.out.println("管道刷新成功~");
        }catch (IOException e){
            e.printStackTrace();
        }
    }

char[] chars = new char[1024*1024];解释:定义一个1024长度的数组,每个元素是一个1024长度的char类型数组,所以元素总数量:1024×1024 = 1,048,576,在Java中char是16位的Unicode字符,占用2个字节。所以该数组总字节是2,097,152字节,1MB(兆字节)等于1024KB(千字节),而1KB等于1024B(字节)
1MB(兆字节) = 1,048,576字节(即 1024×1024)。
2,097,152字节 = 2,097,152 ÷ 1,048,576 = 2MB
因此:用在上面例子中,即每次拷贝已2兆的大小转移

byte[] byters = new byte[1024*1024]。byte占用1个字节。这个是分配大约1MB的内存空间来存储字符数据

动态数组

为解决静态数组长度固定的问题,引入动态数组,Java 的 ArrayList,ArrayList是基于数组实现的动态数组,自动扩容:当容量不足时,按比例(如翻倍)扩展内存。其实现的机制原理是如下示例

步骤1:初始无参构造:数组为空

	// 创建ArrayList
    ArrayList<String> str= new ArrayList<String>();

源码底层实现逻辑,创建一个

	// 调用初始化无参构造方法。创建一个空数组elementData 并赋以 初始默认容量数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    //定义一个元素数据 这样一个初始数组
    transient Object[] elementData; 
	//ArrayList定义的静态常量空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

arrayList 没有提供查看容量大小方法,我们通过反射方式看下容量大小,即elementData长度

    @Test
    public void testQ() throws NoSuchFieldException, IllegalAccessException {
        //可以在这里的构造函数来进行ArrayList初始容量的设定
        ArrayList<String> str = new ArrayList<String>();
        System.out.println("初始化容量"+getArrayListCapacity(str));
    }
    // 通过反射获取elementData值
    public static int getArrayListCapacity(ArrayList<?> arrayList) {
        Class<ArrayList> arrayListClass = ArrayList.class;
        try {
            Field field = arrayListClass.getDeclaredField("elementData");
            field.setAccessible(true);
            Object[] objects = (Object[])field.get(arrayList);
            return objects.length;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

打印结果:初始化容量 0

步骤2:添加元素:容量动态扩容

        str.add("a");
        System.out.println("首次添加元素容量:"+getArrayListCapacity(str)
        				+",元素个数:"+str.size());

首次添加元素容量:10,元素个数:1
底层实现原理

public boolean add(E e) {
	//1. 首次添加元素,原size为0,即size+1 = 1;
	ensureCapacityInternal(size + 1); 
	// 2. 元素存入数组索引位0,并更新size变为1
    elementData[size++] = e;   
    // 3. 始终返回true         
    return true;                       
}

ensureCapacityInternal(int minCapacity): :确保内部容量,上面通过size+1,即每次添加元素 就用原元素数量+1 作为 最小容量需求作为参数 传入该方法

// ensureCapacityInternal 翻译:确保内部容量
private void ensureCapacityInternal(int minCapacity) {
	// 通过和ArrayList初始默认数组对象比较,判断是否第一次添加元素
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 
    	// 扩容至默认容量DEFAULT_CAPACITY = 10
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
    }
    // 然后开始 显示处理容量大小
    ensureExplicitCapacity(minCapacity);
}

ensureExplicitCapacity(int minCapacity)

private void ensureExplicitCapacity(int minCapacity) {
//并发安全:modCount用于检测并发修改,迭代中修改集合会抛出ConcurrentModificationException
    modCount++; 
    //如果最小容量需求 大于 现有数组容量,则进行自动扩容,否则不用扩容量
    if (minCapacity - elementData.length > 0)
        grow(minCapacity); // 触发扩容
}

到这里minCapacity =10 ,elementData.length =0 大于0满足 开始执行自动扩容grow 方法

// 自动扩容方法
private void grow(int minCapacity) {
	//原有数组容量
    int oldCapacity = elementData.length;
    // 1.5倍扩容容量
    int newCapacity = oldCapacity + (oldCapacity >> 1); 
    // 确保满足最小需求,若1.5倍仍不足,直接使用所需最小容量minCapacity
    if (newCapacity < minCapacity) newCapacity = minCapacity; 
    
    // 数组上限处理:确保新容量不超过MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),
    // 否则调整至Integer.MAX_VALUE。
    if (newCapacity > MAX_ARRAY_SIZE) newCapacity = hugeCapacity(minCapacity);
    
    // 将旧数组数据复制到新数组中,完成扩容
    elementData = Arrays.copyOf(elementData, newCapacity); 
}

Ⅰ.首先添加元素 容量扩容至 默认长度10,触发grow方法 复制到新数组
Ⅱ.添加第11个元素,原容量10不足,再次触发触发grow方法,扩容至15(10 + 5),元素存入索引10,size变为11。

总结
1.数组本身是静态的,无法自动扩容的,初始容量多少就是多少
2.日常需要中无法确认一个数组我们需要多少容量,或者容量的大小是经常变动的,才有了所谓的动态数组,这种动态扩容机制是一种设计理念,其本质是根据最新需求容量比较原有数组容量,返回一个拷贝原数组元素的新数组,Arrays.copyOf返回的是new Object。这种动态扩容机制在StringBuffer等,周期线程池的无阻塞延迟队列等都有使用

常见方法

数组的使用在java中比较多,也封装了很多关于数组方法的类,如Arrays,ArrayList,ArrayDeque,ArrayUtils等,像ArrayList 数组和List集合结合的类,ArrayDeque数组和队列结合的类,这部分在集合中介绍,下面介绍Arrays和ArrayUtils

Arrays

包位于: java.util.Arrays,这是一个JDK自带的工具类,包含了操纵数组的各种方法
核心功能

  • 排序:支持基本类型和对象数组的排序。
  • 搜索:在已排序的数组中快速查找元素。
  • 填充:将数组的所有或部分元素填充为指定值。
  • 比较:判断两个数组是否相等。
  • 复制:生成数组的副本。
  • 转换:将数组转换为列表(List)或字符串(String)。
  • 流操作:支持将数组转换为流(Java 8+)。

排序

1.sort() 排序

功能:数组进行升序排序(支持基本类型和对象

①基本数据类型数组排序
适用于所有基本类型数组(byte, short, int, long, float, double, char),按自然顺序升序排序

int[] numbers = {3, 1, 4, 1, 5};
Arrays.sort(numbers); 
System.out.println(Arrays.toString(numbers));// 排序后打印:[1, 1, 3, 4, 5]

char[] chars = {'z', 'a', 'D', '#'};
Arrays.sort(chars); 
// 结果:['#', 'D', 'a', 'z'](按Unicode值排序)

底层实现

    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }

算法实现:基于双轴快速排序(Dual-Pivot Quicksort),适用于基本类型数组(如 int[]、long[])

②对象数组排序(自然顺序)
对象必须实现Comparable接口,按compareTo方法定义的顺序排序
示例

String[] names = {"Alice", "Bob", "Charlie", "David"};
Arrays.sort(names); 
// 结果:["Alice", "Bob", "Charlie", "David"](字典序)

class Person implements Comparable<Person> {
    String name;
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
}
Person[] people = ...; 
Arrays.sort(people); // 按name字段排序

方法源码

    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }

TimSort(优化后的归并排序) ,时间复杂度:O(n log n),稳定排序(相等元素顺序不变)

③对象数组排序(自定义顺序)
通过Comparator自定义排序规则,支持灵活排序逻辑
方法签名

static <T> void sort(T[] a, Comparator<? super T> c)

示例

Integer[] numbers = {5, 3, 9, 1, 6};
// 降序排序
Arrays.sort(numbers, (a, b) -> b - a); 
// 结果:[9, 6, 5, 3, 1]

// 按字符串长度排序
String[] words = {"apple", "banana", "kiwi"};
Arrays.sort(words, Comparator.comparingInt(String::length)); 
// 结果:["kiwi", "apple", "banana"]

④部分范围排序
对数组的指定区间进行排序,适用于仅需处理子数组的场景。
方法签名

static void sort(Xxx[] a, int fromIndex, int toIndex) // 基本类型
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) // 对象类型

参数说明
fromIndex:起始索引(包含)
toIndex:结束索引(不包含)
异常:若索引越界,抛出IllegalArgumentException或ArrayIndexOutOfBoundsException

2.parallelSort() 排序

支持基本数据 和 对象,和sort方法区别在于 并行排序(大数据量优化),数据量较大(通常超过 10万元素),适用多核CPU环境(核心数 ≥ 4)

static void parallelSort(Xxx[] a) // 基本类型
static <T> void parallelSort(T[] a, Comparator<? super T> cmp) // 对象类型

特点:
①数据规模阈值:Java内部对parallelSort设置了阈值MIN_ARRAY_SORT_GRAN = 8192),小于该值会自动退化为 sort

        if (n <= MIN_ARRAY_SORT_GRAN ||
            (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
            DualPivotQuicksort.sort(a, 0, n - 1);//小于阈值会自动退化为 sort

② 对象排序:对象数组需实现 Comparable 接口或传入 Comparator

总结:
在这里插入图片描述

查找:binarySearch()

binarySearch():二分查找
功能:在已排序的数组中查找元素,返回索引(未找到返回负数)

int[] sorted = {1, 2, 3, 4, 5};
int index1 = Arrays.binarySearch(sorted, 3); // 返回 2
int index2 = Arrays.binarySearch(sorted, 30); // 返回 -6

填充:fill()

fill():将数组的所有或部分元素填充为指定值。

int[] arr = new int[5];
Arrays.fill(arr, 0);        // 填充为 [0, 0, 0, 0, 0]
Arrays.fill(arr, 1, 3, 9);  // 填充索引1到2为9 → [0, 9, 9, 0, 0]

比较:equals() 和 deepEquals()

功能:判断两个数组内容是否相等。
equals():比较一维数组。
deepEquals():深度比较多维数组。

int[] a = {1, 2};
int[] b = {1, 2};
boolean isEqual = Arrays.equals(a, b); // true

int[][] matrix1 = {{1, 2}, {3, 4}};
int[][] matrix2 = {{1, 2}, {3, 4}};
boolean isDeepEqual = Arrays.deepEquals(matrix1, matrix2); // true

复制:copyOf() 和 copyOfRange()

功能:生成数组的副本或部分副本。

int[] original = {1, 2, 3, 4, 5};
int[] copy1 = Arrays.copyOf(original, 3);        // 复制前3个→索引 0 1 2 → [1, 2, 3]
int[] copy2 = Arrays.copyOfRange(original, 1, 4); // 复制索引1到3 → [2, 3, 4]

转换为列表:asList()

功能:将数组转换为固定长度的 List(不可增删元素

String[] names = {"Alice", "Bob", "Charlie"};
List<String> list = Arrays.asList(names); // 支持遍历和修改元素,但不可增删

names[0] = "du"; // list打印 就变成 [du, Bob, Charlie]
list.add("du");// 这里会抛 UnsupportedOperationException

asList() 的陷阱:
1.返回的 List 是基于原始数组的视图,修改数组会影响 List
2.不能对返回的 List 进行增删操作(会抛 UnsupportedOperationException)

转换为字符串:toString() 和 deepToString()

功能:将数组内容转换为可读的字符串形式。两者区别在于作用对象是一维数组和多维数组

int[] arr = {1, 2, 3};
System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 3]

int[][] matrix = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepToString(matrix)); // 输出 [[1, 2], [3, 4]]

多维数组操作方法:使用 deepToString()、deepEquals() 和 deepHashCode() 处理多维数组。这种方法名字带有 deep都是对多维数组的

流操作:stream()

功能:将数组转换为流(Stream),支持函数式编程。

int[] numbers = {1, 2, 3, 4, 5};
int sum = Arrays.stream(numbers).sum(); // 求和 → 15

Arrays总结

Arrays 类是 Java 中通过静态方法封装了数组的常见操作,避免了开发者重复造轮子。它的核心优势包括:
1.代码简洁性:一行代码完成复杂操作(如排序、填充)。
2.性能优化:底层使用高效算法(如快速排序、二分查找)。
3.安全性:避免直接操作数组时的越界错误。
使用建议:在需要处理数组的简单操作时,优先使用 Arrays 类;对于复杂动态数据需求,可结合 ArrayList 或 Stream API

ArrayUtils

位于:commons-lang包 org.apache.commons.lang.ArrayUtils,说明这个是 apache分布的一个辅助包,其功能看名字也是Array工具类,Arrays工具类提供对数组的基本操作方法,ArrayUtils 通过封装底层数组操作,类似于StringUtils,需要进行依赖引入,依赖引入:Apache Commons Lang 3 需添加 Maven 依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

常见方法介绍

ArrayUtils中的方法:
1.add():将给定的数据添加到指定的数组中,返回一个新的数组。
2.addAll():合并两个数组。
3.contains():检查该数据在该数组中是否存在,返回一个boolean值。
4.getLength():返回该数组长度。
5.indexOf():从数组的第一位开始查询该数组中是否有指定的数值,存在返回index的数值,否则返回-16.lastIndexOf():从数组的最后一位开始往前查询该数组中是否有指定的数值,存在返回index的数值,否则返回-17.Insert():向指定的位置往该数组添加指定的元素,返回一个新的数组。
8.isEmpty():判断该数组是否为空,返回一个boolean值。
9.isNotEmpty():判断该数组是否为空,而不是null10.isSameLength():判断两个数组的长度是否一样,当数组为空视长度为0。返回一个boolean值。
11.isSameType():判断两个数组的类型是否一样,返回一个boolean值。
12.isSorted():判断该数组是否按照自然排列顺序排序,返回一个boolean值。
13.nullToEmpty():
14.remove():删除该数组指定位置上的元素,返回一个新的数组。
15.removeAll():删除指定位置上的元素,返回一个新的数组。
16.removeAllOccurences():从该数组中删除指定的元素,返回一个新的数组。
17.removeElement():从该数组中删除第一次出现的指定元素,返回一个新的数组。
18.removeElements():从该数组中删除指定数量的元素,返回一个新的数组。
19.reverse():数组反转。也可以指定开始和结束的反转位置。
20.subarray():截取数组(包头不包尾),返回一个新的数组。
21.swap():指定该数组的两个位置的元素交换或者指定两个位置后加len的长度元素进行交换。
22.toMap():将数组转换成Map,返回一个map的Object的集合。
23.toObject():将原始数据类型的数组转换成对象类型数组。
24.toPrimitive():将对象类型数组转换成原始数据类型数组。
25.toString():将数组输出为Stirng,返回一个字符串。
26.toStringArray():Object数组转换为String数组类型。

相关文章:

  • 版本控制工具——Git
  • 买不起了,iPhone 或涨价 40% ?
  • C++ 模板的应用——智能指针、STL库
  • MySQL学习笔记五
  • 电脑DNS出错无法打开网页
  • 笔记 2025/4/7
  • 构建一个最简单的UDP服务器和客户端并逐行解析
  • “暂不能解析域名”的错误的常见原因及解决方法
  • Spring Boot 项目日志系统全攻略:Logback、Log4j2、Log4j与SLF4J整合指南
  • Java中使用Function Call实现AI大模型与业务系统的集成​
  • SQL:数据类型(Data Types)
  • 懂x帝二手车数据爬虫-涉及简单的字体加密,爬虫中遇到“口”问题的解决
  • 可发1区的超级创新思路(python 实现):基于时空解耦和对比学习的可解释性模型
  • AI大模型底层技术——结合 Prompt Engineering 的 LoRA
  • 《C++速通秘籍》
  • GD32F407开发环境搭建
  • 基于Android的图书借阅和占座系统(源码+lw+部署文档+讲解),源码可白嫖!
  • C++ 环境设置
  • Linux中查看占用端口号的进程信息的方法
  • ✅ Ultralytics YOLO验证(Val)时自动输出COCO指标(AP):2025最新配置与代码详解 (小白友好 + B站视频)
  • 创建一个网站的步骤是/网站优化策略分析
  • 长安做网站/seo在哪学
  • 建设工程质量安全管理协会网站/农产品网络营销推广方案
  • 南昌网站建设信息/百度爱采购推广一个月多少钱
  • 聊城网站建设项目/腾讯营销平台
  • 网站开发 经常要清理缓存/黄页污水