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兆的大小转移
动态数组
为解决静态数组长度固定的问题,引入动态数组,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的数值,否则返回-1。
6.lastIndexOf():从数组的最后一位开始往前查询该数组中是否有指定的数值,存在返回index的数值,否则返回-1。
7.Insert():向指定的位置往该数组添加指定的元素,返回一个新的数组。
8.isEmpty():判断该数组是否为空,返回一个boolean值。
9.isNotEmpty():判断该数组是否为空,而不是null。
10.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数组类型。