Java数组详解
1. 数组概述
数组是同一种类型数据的集合,用来存储同类型数据的容器。我们将容器中的数据称为元素。
数组的特点
-
数组没有API:数组不是对象,而是一堆数据的集合容器,因此没有可查阅的API文档。
-
唯一的length属性 :数组只有一个
length属性(注意不是length()方法)。数组的长度在初始化时就固定了,无法改变。 -
没有内置方法 :数组本身没有任何方法,但我们可以使用
Arrays工具类来操作数组。该类提供了数组相关的一系列静态方法。
2. 数组定义
定义方式一:指定长度
java
元素类型[] 数组名 = new 元素类型[元素个数或者数组长度];
示例:
java
int[] arr = new int[4];
定义方式二:直接初始化
java
元素类型[] 数组名 = new 元素类型[]{元素1, 元素2, ......};
示例:
java
int[] arr = new int[]{3, 4, 2, 8};
简化写法:
java
int[] arr = {3, 4, 2, 8};
3. 数组常用操作
访问和修改元素
java
数组名[角标位置] // 获得角标位置上的元素
获取完该元素后,也可以将同类型的指定元素赋值给此元素,来改变数组内容。
获取数组长度
java
数组名.length // 获取数组长度
4. Arrays工具类详解
概述
Arrays类位于java.util包中,专门用于数组操作。该类只包含静态方法。
主要功能
为了扩展数组的应用,可以调用Arrays.asList()方法将数组转换成List集合,使用集合的思想和方法来操作数组元素。
常用方法
| 方法名 | 功能描述 |
|---|---|
binarySearch() |
折半查找 |
copyOf() |
复制数组 |
copyOfRange() |
复制指定范围的数组 |
equals() |
比较两个数组中的元素是否相同 |
deepEquals() |
深度比较 |
fill() |
替换值 |
hashCode() |
获取哈希值 |
sort() |
对数组进行升序排序 |
toString() |
转换为字符串表示 |
asList(T... a) |
将数组转换为List集合 |
**注意:**集合转数组使用toArray()方法。
Arrays.asList()使用注意事项
⚠️ 重要提醒:
-
不能使用增删方法 :将数组变成集合后,不能使用集合的增删方法,因为数组长度固定。如果尝试增删会抛出
UnsupportedOperationException异常。 -
对象数组转换:如果数组中的元素都是对象,数组中的元素会直接转成集合中的元素。
-
基本数据类型数组转换:如果数组中的元素都是基本数据类型,那么该数组作为集合中的元素存入。
5. 数组排序算法
以下是常见的数组排序算法实现:
完整示例代码
java
public class SortDemo {
int array[] = {11, 22, 55, 44, 33, 4, 5, 6, 2, 43, 64, 7};
int index = array.length;
public static void main(String[] args) {
SortDemo sd = new SortDemo();
// 选择要使用的排序算法
// sd.bubbleSort(); // 1、冒泡排序
// sd.selectionSort(); // 2、选择排序
// sd.insertionSort(); // 3、插入排序
sd.mergeSort(); // 4、归并排序
// sd.shellSort(); // 5、希尔排序
// sd.quickSort(); // 6、快速排序
// 输出结果
for (int i = 0; i < sd.array.length; i++) {
System.out.print(sd.array[i] + " ");
}
}
// 交换两个元素位置的工具方法
public void swap(int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
}
1. 冒泡排序(Bubble Sort)
算法原理:
- 逐次比较两个相邻数据的大小并交换位置
- 每次比较都找出当前范围内的最大项
- 时间复杂度:O(N²)
java
public void bubbleSort() {
int out, in;
for (out = index - 1; out > 1; --out) {
for (in = 0; in < out; ++in) {
if (array[in] > array[in + 1]) {
swap(in, in + 1);
}
}
}
}
2. 选择排序(Selection Sort)
算法原理:
- 对冒泡排序的优化版本
- 每次遍历记录最小值位置,遍历结束后再交换
- 减少了交换次数,只需要N次交换
- 时间复杂度:O(N²)
java
public void selectionSort() {
int out, in;
for (out = 0; out < index - 1; ++out) {
int temp = out;
for (in = out + 1; in < index; ++in) {
if (array[in] < array[temp]) {
temp = in;
}
}
swap(out, temp);
}
}
3. 插入排序(Insertion Sort)
算法原理:
- 充分利用已排列好的数据
- 将未排序的数据插入到已排序队列的正确位置
- 每插入一个数据,已排序队列增加一个成员
java
public void insertionSort() {
int out, in;
for (out = 1; out < index; ++out) {
int temp = array[out];
in = out;
while (in > 0 && temp < array[in - 1]) {
array[in] = array[in - 1];
--in;
}
array[in] = temp;
}
}
4. 归并排序(Merge Sort)
算法原理:
- 将两个有序数组合并为一个有序数组
- 结合二分法递归地将数组依次归并
- 时间复杂度:O(N log N)
- 空间复杂度:需要额外开辟一倍的存储空间
java
public void mergeSort() {
int[] newArray = new int[index];
recMergeSort(newArray, 0, index - 1);
}
private void recMergeSort(int[] data, int low, int upper) {
if (low == upper) {
return;
}
int mid = (low + upper) / 2;
recMergeSort(data, mid + 1, upper);
recMergeSort(data, low, mid);
merge(data, low, mid + 1, upper);
}
private void merge(int[] data, int lowStart, int highStart, int upperBound) {
int j = 0;
int lowBound = lowStart;
int mid = highStart - 1;
int num = upperBound - lowStart + 1;
while (lowStart <= mid && highStart <= upperBound) {
if (array[lowStart] < array[highStart]) {
data[j++] = array[lowStart++];
} else {
data[j++] = array[highStart++];
}
}
while (lowStart <= mid) {
data[j++] = array[lowStart++];
}
while (highStart <= upperBound) {
data[j++] = array[highStart++];
}
for (j = 0; j < num; j++) {
array[lowBound + j] = data[j];
}
}
5. 希尔排序(Shell Sort)
算法原理:
- 插入排序的高级版本
- 将每次比较的间隔扩大,让数据在大范围内移动
- 逐渐缩小间隔,最终完成排序
- 使用Knuth间隔序列:h = h * 3 + 1
java
public void shellSort() {
int in, out;
int h = 1;
int temp;
// 计算最大间隔
while (h < index / 3) {
h = h * 3 + 1;
}
while (h > 0) {
for (out = h; out < index; ++out) {
temp = array[out];
in = out;
while (in > h - 1 && array[in - h] > temp) {
array[in] = array[in - h];
in -= h;
}
array[in] = temp;
}
h = (h - 1) / 3;
}
}
6. 快速排序(Quick Sort)
算法原理:
- 选择一个基准值(pivot)
- 将小于pivot的数据放在左侧,大于pivot的放在右侧
- 对左右两侧分别递归执行相同策略
- 平均时间复杂度:O(N log N)
java
public void quickSort() {
recQuickSort(0, index - 1);
}
private void recQuickSort(int left, int right) {
int size = right - left + 1;
if (size <= 3) {
manualSort(left, right);
} else {
int pivot = median(left, right);
int partition = partition(left, right, pivot);
recQuickSort(left, partition - 1);
recQuickSort(partition + 1, right);
}
}
private int partition(int left, int right, int pivot) {
int leftPtr = left;
int rightPtr = right - 1;
while (true) {
while (array[++leftPtr] < pivot);
while (array[--rightPtr] > pivot);
if (leftPtr >= rightPtr) {
break;
} else {
swap(leftPtr, rightPtr);
}
}
swap(leftPtr, right - 1);
return leftPtr;
}
private int median(int left, int right) {
int center = (left + right) / 2;
if (array[left] > array[center]) {
swap(left, center);
}
if (array[left] > array[right]) {
swap(left, right);
}
if (array[center] > array[right]) {
swap(center, right);
}
swap(center, right - 1);
return array[right - 1];
}
private void manualSort(int left, int right) {
int size = right - left + 1;
if (size <= 1) return;
if (size == 2) {
if (array[left] > array[right])
swap(left, right);
} else {
if (array[left] > array[right - 1])
swap(left, right - 1);
if (array[left] > array[right])
swap(left, right);
if (array[right - 1] > array[right])
swap(right - 1, right);
}
}
6. 常见异常
ArrayIndexOutOfBoundsException
角标越界异常
- 原因: 操作数组时访问了数组中不存在的索引位置
- 解决: 确保访问的索引在 0 到 array.length-1 的范围内
NullPointerException
空指针异常
- 原因: 当引用没有指向任何对象(值为null)时,仍然尝试使用该引用操作实体
- 解决: 在使用数组前先判断是否为null
总结
数组是Java中最基础的数据结构之一,掌握数组的操作和各种排序算法对提升编程能力非常重要。在实际开发中:
- 优先使用
Arrays工具类进行数组操作 - 根据数据规模选择合适的排序算法
- 注意数组越界和空指针异常的防范
- 合理使用数组转集合的功能,但要注意其限制
本文整理了Java数组的核心知识点,包括基本概念、常用操作、工具类使用和各种排序算法的实现,希望对Java学习者有所帮助。