java数组常用操作(其三)
初始化数组
在Java 中的数组初始化是指为数组分配内存空间并给每个元素赋予初始值的过程。这一过程是创建数组对象时不可或缺的一部分,因为只有经过初始化之后,数组才能被正常地使用。在 Java 中,数组初始化主要分为静态初始化和动态初始化两种方式,此外还存在默认初始化的概念。
静态初始化
静态初始化指的是在声明数组的同时为每个元素指定具体的初始值,此时数组的长度由系统根据提供的值的数量自动决定。静态初始化有两种形式:
使用 new
关键字:这种方式下,程序员通过 new
操作符创建一个数组,并立即用大括号 {}
包围的列表来提供各元素的具体值。
int[] intArr;
intArr = new int[]{1, 2, 3, 4, 5, 9};
简化形式:直接在声明数组变量时给出初始值列表,而不需要显式地使用 new
关键字。
String[] strArr = {"张三", "李四", "王五"};
动态初始化
动态初始化是指仅指定数组的长度而不立即提供具体元素值的情况,这时每个元素会被赋予相应的默认值(如整数类型的默认值为 0
,布尔类型为 false
等)。
int[] price = new int[4];
默认初始化
当数组通过动态初始化创建后,如果没有明确地为每个元素指定初始值,那么它们将会获得与其数据类型相匹配的默认值。对于基本数据类型而言,这通常意味着数值类型会得到零值,布尔类型会得到 false
,而对于引用类型则会得到 null
。这意味着一旦数组完成初始化,即使没有显式地为每个元素赋值,每个元素都已经有了一个确定的状态。
注意事项
-
不应该同时采用静态初始化和动态初始化的方式,即不应该在同一行代码中既指定了数组的长度又为每个元素提供了具体的初始值。
-
数组一旦初始化完毕,其大小(即长度)就固定下来了,无法再改变。
访问元素
在Java中访问数组元素是通过索引实现的,这是一个基本且重要的操作。数组中的每个元素都有一个唯一的索引值来标识它的位置,这个索引是从0开始的整数序列。
这意味着如果你有一个长度为n的数组,那么第一个元素的索引就是0,而最后一个元素的索引则是n-1。例如,如果我们有一个名为arr
的数组,并且想要访问它的第一个元素,我们可以使用arr[0]
;如果要访问第二个元素,则可以使用arr[1]
。
当访问数组元素时,除了直接通过索引获取单个元素外,还可以利用循环结构遍历整个数组或部分数组。这允许我们对所有元素执行相同的操作,比如打印、计算总和或者查找最大值等。以下是几种常见的遍历方法:
普通for循环:
这是最传统的遍历方式,它允许我们明确地控制索引变量,从而可以方便地访问特定位置上的元素。
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
增强型for循环(foreach):
从Java 5开始引入了这种更简洁的方式,它可以简化代码并且避免了显式地处理索引。
Arrays.stream(arr).forEach(System.out::println);
注意事项
-
如果你需要获取数组中最后一个元素,可以直接使用
arr[arr.length - 1]
,这里利用了数组自带的length
属性来确定最后一个有效索引。 -
当你试图访问超出数组边界范围的索引时(即小于0或大于等于
arr.length
),Java会抛出ArrayIndexOutOfBoundsException
异常。因此,在编写程序时务必确保所使用的索引是在合法范围内。 -
对于多维数组而言,访问其内部元素需要指定多个索引。例如,对于二维数组
matrix
来说,你可以用matrix[rowIndex][columnIndex]
来访问某一行某一列处的元素
插入元素
在Java中,数组的长度是固定的,一旦创建之后就不能改变其大小。这意味着你不能直接向现有的数组中添加新元素。然而,可以通过几种间接的方法来实现插入元素的效果。
以下是几种常见的方法:
方法一:使用 ArrayList
// 将数组转换为 ArrayList
List<Integer> list = new ArrayList<>(Arrays.asList(originalArray));
// 在指定位置插入元素
list.add(index, element);
// 将 ArrayList 转换回数组
Integer[] newArray = list.toArray(new Integer[0]);
方法二:创建新数组并复制元素
//创建比原数组大 1 的新数组
int[] newArray = new int[originalArray.length + 1];
//使用System.arraycopy复制元素
System.arraycopy(originalArray, 0, newArray, 0, index); // 复制前半部分
System.arraycopy(originalArray, index, newArray, index + 1, originalArray.length - index); // 复制后半部分
//设置插入位置的新值
newArray[index] = element;
方法三:使用 Apache Commons Lang
库中的 ArrayUtils
如果项目允许引入第三方库,那么可以考虑使用 Apache Commons Lang 提供的 ArrayUtils
类。这个类包含了许多静态方法,可以帮助简化数组操作,包括插入元素。例如:
int[] newArray = ArrayUtils.insert(index, originalArray, element);
总结:
在 Java 中插入数组元素并没有直接的方式,但我们可以通过上述三种方法之一来间接实现这一目标。每种方法都有其适用场景和优缺点,开发者可以根据具体需求选择最合适的方法。例如,如果你的应用程序经常需要对数组进行插入和删除操作,那么可能更适合采用 ArrayList
;而对于那些对性能要求较高的场合,或者当插入操作不频繁发生时,创建新数组并手动复制元素可能是更好的选择。此外,如果希望减少依赖并且代码库中已经集成了 Apache Commons Lang,那么使用 ArrayUtils
可能会是最方便的选择。
删除元素
由于Java中的数组长度是固定的,直接从数组中“删除”一个元素实际上意味着创建一个新的数组或调整现有元素的位置来模拟删除效果。
使用 ArrayList
:
可以将数组转换为 ArrayList
,然后调用 remove()
方法移除指定位置的元素。最后再将 ArrayList
转换回数组。
List<Integer> list = new ArrayList<>(Arrays.asList(originalArray));
list.remove(index); // 删除指定索引处的元素
Integer[] newArray = list.toArray(new Integer[0]);
手动复制和移动元素:
对于原始类型的数组,可以通过创建一个新的数组,并使用 System.arraycopy()
来复制旧数组中的元素到新数组中,跳过要删除的那个元素。这种方法适用于希望避免引入额外库的情况。
int[] newArray = new int[originalArray.length - 1];
System.arraycopy(originalArray, 0, newArray, 0, index);
if (index < originalArray.length - 1) {
System.arraycopy(originalArray, index + 1, newArray, index, originalArray.length - index - 1);
}
覆盖删除:
如果不需要保持数组的连续性或者不关心被删除元素之后的数据,则可以直接用最后一个元素覆盖待删除的元素,并减少数组的有效长度(仅适用于自定义逻辑控制数组大小的情况)。
遍历数组
遍历数组是指访问数组中的每一个元素,通常用于执行某些操作如打印、计算总和等。
for循环:
最传统的方式,通过索引访问每个元素。
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
增强型for循环(foreach):
简化语法,适合于不需要知道当前索引值的情况。
for (Type element : arr) {
System.out.println(element);
}
Stream API:
自Java 8以来提供的流式编程接口,支持函数式编程风格。
Arrays.stream(arr).forEach(System.out::println);
此外还有其他高级遍历技术,比如使用迭代器 Iterator
或者结合Lambda表达式的 forEach()
方法来进行更灵活的操作。
查找元素
查找元素是指定位数组中某个特定值的位置或判断该值是否存在。Java提供了若干内置方法来完成这项任务:
indexOf()
和 lastIndexOf()
:
分别返回首次出现和最后一次出现的目标元素的索引。如果没有找到则返回 -1
。
int firstIndex = Arrays.asList(arr).indexOf(target);
int lastIndex = Arrays.asList(arr).lastIndexOf(target);
contains()
:
检查数组是否包含某个元素,但需注意这只能用于对象数组,因为基本类型数组没有此方法。
boolean contains = Arrays.asList(arr).contains(target);
二分查找:
适用于已排序的数组,效率较高,时间复杂度为 O(log n) 。需要先对数组进行排序。
Arrays.sort(arr);
int index = Arrays.binarySearch(arr, target);
自定义查找条件:
利用 stream()
结合谓词(Predicate)或其他条件表达式来筛选符合条件的元素。
Optional<Type> result = Arrays.stream(arr).filter(x -> x == target).findFirst();
扩容数组
由于Java数组的长度不可变,因此当需要增加数组容量时,必须创建一个新的更大容量的数组,并将原有数组的内容复制过来。常用的扩容策略包括但不限于:
手动复制:
简单地创建一个更大的新数组,并使用 System.arraycopy()
将旧数组的所有元素复制进去。
int[] newArray = new int[oldArray.length * 2]; // 假设扩容一倍
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
Arrays.copyOf()
:
这是 Java 标准库提供的便捷方法,可以轻松地创建一个具有指定新长度的新数组副本。
int[] newArray = Arrays.copyOf(oldArray, newSize);
ArrayList
动态管理:
虽然这不是严格意义上的数组扩容,但 ArrayList
内部实现了自动扩容机制,每当添加新元素而空间不足时,它会自动增长数组的大小。默认情况下,每次扩容大约是原来大小的1.5倍左右。