1.数组的基础知识及用法
1.1 为什么要有数组?
- 为了存储同种类型的多个值
数组的概念: 数组是存储同一种数据类型多个元素的集合。也可以看成是一个容器。数组既可以存储基本数据类型,也可以存储引用数据类型。
1.2 创建数组
//动态初始化
数据类型[ ] 数组名 = new 数据类型[数组的长度];
//静态初始化
数据类型[ ] 数组名 = {初始化数据};
代码示例
int[] arr = new int[];//动态初始化
- 注意动态初始化只指定长度,由系统给出初始化值。
int[] arr = {0,1,2,3,4};//静态初始化
- 静态初始化的时候,数组元素个数与初始化数据的格式是一致的。
1.3 数组的使用
代码示例:获取长度&访问元素
int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length); // 执行结果: 3
// 访问数组中的元素
System.out.println(arr[1]); // 执行结果: 2
System.out.println(arr[0]); // 执行结果: 1
arr[2] = 100;
System.out.println(arr[2]); // 执行结果: 100
注意:下标访问操作不能超出有效范围[0,length - 1],如果超出有效范围,会出现下标越界异常(ArrayIndexOutOfBoundsException)。
代码示例:下标越界
int[] arr = {0,1,2,3};
System.out.println(arr[10]);
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at Test.main(Test.java:4)
代码示例:空指针异常
int[] arr = {1,2,3};
arr = null;
System.out.println(arr[0]);
//NullPointerException
原因:数组已经不在指向堆内存了,而你还用数组名去访问元素.
代码示例:遍历数组
所谓“遍历”是指将数组中的所有元素都访问一边,通常需要搭配循环语句。
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 执行结果
1
2
3
* 数组的属性:arr.length数组的长度
* 数组的最大索引:arr.length - 1;
代码示例:for-each遍历数组
int[] arr = {1, 2, 3};
for (int x : arr) {
System.out.println(x);
}
// 执行结果
1
2
3
for-each是for循环的另一种使用方式,能够更方便的完成对数组的遍历,可以避免循环条件和更新语句写错。
代码示例:获取最值
public static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1;i < arr.length ;i++ ) { //从数组的第二个元素开始遍历
if (max < arr[i]) { //如果max记录的值小于的数组中的元素
max = arr[i]; //max记录住较大的
}
}
return max;
}
2.数组的拷贝
2.1 clone 方法
基本数据类型(int,boolean,char,byte,short,float,double,long)都可以直接使用clone方法进行克隆,注意String类型是因为其值不可变才可以使用。
代码示例(int类型):
int[] a1 = {1, 3};
int[] a2 = a1.clone();
a1[0] = 666;
System.out.println(Arrays.toString(a1)); //输出结果[666, 3]
System.out.println(Arrays.toString(a2)); //输出结果[1, 3]
2.2 System.arraycopy
System.arraycopy方法是一个本地的方法,其使用方法为
System.arraycopy(原数组,原数组的开始位置,目标数组,目标数组的开始位置,拷贝个数)
代码示例:
int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = new int[10];
System.arraycopy(a1, 1, a2, 3, 3);
System.out.println(Arrays.toString(a1)); // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(a2)); // [0, 0, 0, 2, 3, 4, 0, 0, 0, 0]
2.3Arrays.copyOf
Arrays.copyOf的用法为
Arrays.copyOf(原数组,拷贝的个数);
代码示例:
int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = Arrays.copyOf(a1, 2);
System.out.println(Arrays.toString(a1)) // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(a2)) // [1, 2]
使用该方法时无需事先使用new关键字对对象进行内存单元的分配。
2.4 Arrays.copyOfRange
Arrays.copyOfRange的使用方法为
Arrays.copyOfRange(原数组,开始拷贝,拷贝的个数);
代码示例:
int[] a1 = {1, 2, 3, 4, 5};
int[] a2 = Arrays.copyOfRange(a1, 0, 1);
System.out.println(Arrays.toString(a1)) // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(a2)) //[1]
2.5 浅拷贝和深拷贝
浅拷贝:在堆内存中不会分配新的空间,而是增加一个引用变量和之前引用指向相同的堆空间。
深拷贝: 在堆内存中分配新空间,将之前的数组堆内存中的内容拷贝到新的空间中。
以上四种拷贝方法均是深拷贝
**对于数组当中如果是基本类型,那么就是深拷贝。
**对于数组当中如果是引用类型,那么就是浅拷贝。
3 数组的查找及顺序问题
3.1查找数组中指定元素
代码示例:顺序查找
给定一个数组,再给定一个元素,找出该元素在数组中的位置
public static void main(String[] args) {
int[] arr = {1,2,3,10,5,6};
System.out.println(find(arr, 10));
}
public static int find(int[] arr, int toFind) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == toFind) {
return i;
}
}
return -1; // 表示没有找到
}
// 执行结果
3
代码示例:二分查找
针对有序数组, 可以使用更高效的二分查找.
* 啥叫有序数组?
* 有序分为 “升序” 和 “降序”
* 如 1 2 3 4 , 依次递增即为升序.
* 如 4 3 2 1 , 依次递减即为降序.
以升序数组为例, 二分查找的思路是先取中间位置的元素, 看要找的值比中间元素大还是小. 如果小, 就去左边找; 否则就去右边找.
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (toFind > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
return mid;
}
}
// 循环结束, 说明没找到
return -1;
}
// 执行结果
5
3.2数组排序
代码示例:检查数组的有序性
给定一个数组,判断该数组是否有序的(升序)
public static void main(String[] args) {
int[] arr = {1,2,3,10,5,6};
System.out.println(isSorted(arr));
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
代码示例:冒泡排序
*给定一个数组,让数组升(降)排序,
算法思路
每次尝试找到当前待排序区间中最小(或最大)的元素, 放到数组最前面(或最后面).
public static void main(String[] args) {
int[] arr = {9, 5, 2, 7};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr) {
// [0, bound) 构成了一个前闭后开区间, 表示已排序区间
// [bound, length) 构成了一个前闭后开区间, 表示待排序区间
// 每循环一次, 就找到一个合适大小的元素, 已排序区间就增大1.
for (int bound = 0; bound < arr.length; bound++) {
for (int cur = arr.length - 1; cur > bound; cur--) {
if (arr[cur - 1] > arr[cur]) {
int tmp = arr[cur - 1];
arr[cur - 1] = arr[cur];
arr[cur] = tmp;
}
}
} // end for
} // end bubbleSort
// 执行结果
[2, 5, 7, 9]
冒泡排序性能较低. Java 中内置了更高效的排序算法
public static void main(String[] args) {
int[] arr = {9, 5, 2, 7};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
4 二维数组
*二维数组本质上也就是一位数组,只不过每个元素又是一个一位数组。
基本语法
数据类型[][] 数组名称 = new 数据类型 [行数][列数];
代码示例:
int[][] arr = new int[3][2]; //定义一个三行两列的数组
int[][] arr = {{1,2,3},{4,5},{6,7,8,9}};
二维数组与一维数组的用法没有明显差别。