4 数组
4.1 概念
数组:是相同类型数据的有序集合,相同类型的若干个数据,按照一定先后次序排列组合而成。其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们
4.2 特点
- 数组长度是确定的,数组一旦被创建,它的大小就是不可以改变的(有局限性)
- 存放的元素类型相同
- 元素可以是任何数据类型,包括基本类型和引用类型
- 可以存放重复的元素
下标:用来表示数组元素的顺序
- 数组下标是从0开始,第一个元素下标为0,可以通过下标访问数组元素
4.3 数组的创建和应用
4.3.1 数组的初始化
一维数组
等号左边:
- 元素类型[] 数组名 例如:
int[] arr
- 元素类型 数组名[] 例如:
int arr[]
- 元素类型[] 数组名 例如:
等号右边:
静态初始化:(根据元素的个数来确定数组的长度)
- new 元素类型[]{元素,元素…} 例如:
new int[]{1, 3, 5}
,代表有3个int类型元素的数组 - {元素,元素…} 例如:
{1, 3, 5}
,代表是有3个元素的数组
- new 元素类型[]{元素,元素…} 例如:
动态初始化:(根据元素的个数来确定数组的长度)
- new 元素类型[5] 例如:
new String[3]
,代表是一个可以存放3个String类型元素的数组
- new 元素类型[5] 例如:
示例:
int arr[] = new int[3];
int arr[] = new int[]{1, 2, 3};
int[] arr = {4, 5, 6};
二维数组
等号左边:
- 元素类型[] 数组名 例如:
int[][] arr
- 元素类型 数组名[] 例如:
int arr[][]
- 元素类型[] 数组名 例如:
等号右边:
静态初始化:
- new 元素类型[]{元素,元素…} 例如:
new int[]{{1, 2, 3}, {4, 5}, {6}}
- {元素,元素…} 例如:
{{1, 2, 3}, {4, 5}, {6}}
- new 元素类型[]{元素,元素…} 例如:
动态初始化:
- new 元素类型[5] 例如:
new String[3][2]
代表可以存放3个元素,每个元素是可以存放2个String类型数据的数组
- new 元素类型[5] 例如:
示例:
int arr[][] = new int[3][2];
int arr[][] = new int[][]{{1, 2, 3}, {4, 5}, {6}};
int[][] arr = {{1, 2, 3}, {4, 5}, {6}};
4.3.2 数组的访问及遍历
数组的访问
- 通过下标进行访问 例如:
arr[下标]
- 通过下标进行访问 例如:
数组的遍历
for
循环遍历 例如:for (int i = 0; i < 数组.length; i++) {}
- 可以进行数组遍历,并且可以在过程中对数组进行修改
- 定义下标方式进行遍历
增强
for
循环遍历(for each
循环) 例如:for (数据类型 变量名 : 数组) {}
- 只能进行遍历,不能对数据进行修改
for each
遍历的效率比普通for
循环高- 采用声明一个临时的变量来保存元素方式遍历,只能访问元素,不能使用这个变量来改变原来数组中的元素
4.3.3 代码示例
/**
* 数组基本使用
*/
public class ArrayBase {
public static void main(String[] args) {
initArray();
visitArray();
dynamicInit();
traversalArray();
traversalArrayUpdate();
//String[] args 可以接收命令行参数
System.out.println(args); // [Ljava.lang.String;@74a14482
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
// 数组的定义
public static void initArray() {
// 动态初始化
int arr[] = new int[3];
// 静态初始化
int arr1[] = new int[]{1, 2, 3};
int[] arr2 = {4, 5, 6};
}
// 通过下标访问数组
public static void visitArray() {
int[] in = new int[]{23,34,645,234,22,46};
// 下 标 : 0 1 2 3 4 5
System.out.println(in); // [I@1b6d3586 地址
System.out.println(in.toString()); // [I@1b6d3586
System.out.println(in[0]); // 23
System.out.println(in[1]); // 34
// 修改数组中的数据
in[0] = 0;
System.out.println(in[0]); // 0
}
// 动态初始化
public static void dynamicInit() {
// 动态初始化
String str[] = new String[3];
System.out.println(str); // [Ljava.lang.String;@4554617c
System.out.println(str.toString()); // [Ljava.lang.String;@4554617c
// 动态赋值
str[0] = "jason";
str[1] = "lyl";
System.out.println(str[0]); // jason
System.out.println(str[1]); // lyl
System.out.println(str[2]); // null -> String (引用数据类型)类型默认值为 null
int ar[] = new int[3];
System.out.println(ar[0]); // 0 int 类型默认值为 0
}
// 数组的遍历
public static void traversalArray() {
String[] str = new String[]{"张三","李四","王五","23","564"};
/*
* 第一种方式
* for循环遍历 快捷键: fori
* i : 下标
*/
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
/*
* 第二种方式
* foreach循环遍历 快捷键 iter (iterator迭代)
* 把 str 中的数据一个一个给 s,并打印输出
*/
for (String s : str) {
System.out.println(s);
}
// java.util 数组工具类
System.out.println(Arrays.toString(str));
}
// 遍历中修改数据 测试
public static void traversalArrayUpdate() {
int[] arr = new int[]{1, 3, 5};
// for循环修改数组 测试
for (int i = 0; i < arr.length; i++) {
arr[i] += 1;
System.out.println(arr[i]); // 2 4 6
}
System.out.println(Arrays.toString(arr)); // 可以修改 [2, 4, 6]
// foreach循环修改数组 测试
for (int i : arr) {
i += 1;
System.out.println(i); // 3 5 7
}
System.out.println(Arrays.toString(arr)); // 修改失败 [2, 4, 6]
}
}
/**
* 数组常见问题
*/
public class ArrayException {
public static void main(String[] args) {
/*
* 常见问题
*
* 数组下标越界异常
* 报错:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
*/
String[] str = new String[]{"张三","李四",null,"王五"};
// System.out.println(strr[4]);
/*
* 空指针异常
* 报错:java.lang.NullPointerException
*/
String[] str2 = null;
// System.out.println(str2[0]);
// int len = str2.length; // NullPointerException
int len = str.length; // 数组长度跟具体存储的数据多少有关,不是从0开始
System.out.println(len); // 4
}
}
/**
* 二维数组
*/
public class Array2d {
public static void main(String[] args) {
initArray2d();
visitArray2d();
traversalArray2d();
}
// 二维数组的初始化
public static void initArray2d() {
// 动态初始化
// int arr[][] = new int[3][2];
int arr[][] = new int[3][];
arr[0] = new int[2];
arr[1] = new int[2];
arr[2] = new int[2];
// 静态初始化
int arr1[][] = new int[][]{{1, 2, 3}, {4, 5}, {6}};
int[][] arr2 = {{1, 2, 3}, {4, 5}, {6}};
}
// 二维数组的访问及修改
public static void visitArray2d() {
int arr[][] = new int[][]{{1, 2, 3}, {4, 5}, {6}};
// 一维 下标 0 1 2
// 二维 下标 0 1 2 0 1 0
System.out.println(arr[0][2]); // 3
// 修改数据
arr[0][2] = 7;
System.out.println(Arrays.toString(arr)); // [[I@1b6d3586, [I@4554617c, [I@74a14482]
System.out.println(Arrays.toString(arr[0])); // [1, 2, 7]
}
// 二维数组的遍历
public static void traversalArray2d() {
int arr[][] = new int[][]{{1, 2, 3}, {4, 5}, {6}};
// 双重for循环
for (int i = 0; i < arr.length; i++) {
// System.out.println(arr[i]);
for (int j = 0; j < arr[i].length; j++) {
System.out.println(arr[i][j]);
}
}
}
}
/**
* 数组练习
*/
public class ArrayTest {
public static void main(String[] args) {
int[] arr = new int[]{12,23,33,213,11,31,94};
testMaxAndMin(arr);
int sum = testSum(arr);
testAverage(arr, sum);
testReversal(arr);
testToString(arr);
testEqual(arr);
testVarages(110, 119, 120);
testVarages(arr);
}
/**
* 测试:求最值
* @param arr int[]
*/
public static void testMaxAndMin(int[] arr) {
// 求数组最大值
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(max < arr[i]){
max = arr[i];
}
}
System.out.println("max:" + max);
// 求数组最小值
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if(max > arr[i]){
max = arr[i];
}
}
System.out.println("min:" + min);
}
/**
* 测试:求数组总和
* @param arr int[]
* @return
*/
public static int testSum(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
System.out.println("总和: "+sum);
return sum;
}
/**
* 测试:求平均数
* @param arr int[]
* @param sum 数组总和
*/
public static void testAverage(int[] arr, int sum) {
System.out.println("平均数:"+sum/arr.length);
}
/**
* 测试:数组的反转
* @param arr int[]
*/
public static void testReversal(int[] arr) {
System.out.println("原数组:" + Arrays.toString(arr));
for (int x = 0, y = arr.length-1; x<y; x++,y--) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
System.out.println("反转后:" + Arrays.toString(arr));
}
/**
* 测试:遍历数组元素
* @param arr int[]
*/
public static void testToString(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.print("]");
System.out.println();
}
/**
* 测试:判断是否相等
* @param arr int[]
*/
public static void testEqual(int[] arr) {
int[] arr1 = arr;
// 判断两个数组是否相等
boolean bn = Arrays.equals(arr,arr1);
System.out.println("是否相等:"+ bn);
}
/**
* 测试:解析可变长参数列表
* @param arr 可变长参数列表
*/
public static void testVarages(int... arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
/**
* 数组的拷贝
*/
public class ArrayTestCopy {
public static void main(String[] args) {
int[] arr = new int[]{12,23,33,213,11,31,94};
testArrayCopy(arr);
testArraysUtilCopy(arr);
testSystemArrayCopy(arr);
testClone(arr);
testArray2dCopy();
}
/**
* 浅拷贝和深拷贝区别
* @param arr
*/
public static void testArrayCopy(int[] arr) {
// 浅拷贝 - 复制的是引用(引用原来的对象)
int[] arr1 = null;
arr1 = arr;
// 深拷贝 - 复制的是数据(重新创建了一个对象)
int[] arr2 = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arr2[i] = arr[i];
}
System.out.println("原数据 arr:" + arr);
System.out.println("浅拷贝 arr1:" + arr1);
System.out.println("深拷贝 arr2:" + arr2);
// 修改浅拷贝数组,会修改原来数组数据
arr1[0] = 100;
System.out.println("修改浅拷贝后原数据 会改变 arr: " + Arrays.toString(arr));
System.out.println("浅拷贝的数据 arr1: " + Arrays.toString(arr1));
// 修改深拷贝数组,不会修改原来数组数据
arr2[0] = 200;
System.out.println("修改深拷贝后原数据 不会改变 arr: " + Arrays.toString(arr));
System.out.println("深拷贝的数据 arr2: " + Arrays.toString(arr2));
}
/**
* Arrays工具类拷贝方法 - 深拷贝
* @param arr
*/
public static void testArraysUtilCopy(int[] arr) {
int[] arr3 = Arrays.copyOf(arr,arr.length);
System.out.println(arr);
System.out.println(arr3);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr3));
}
/**
* System.arraycopy(src, srcPos, dest, destPos, length);
* 复制数组解析: (原数组,从原数组的起始位置,目标数组,目标数组的起始位置,要复制的数组长度)
* @param arr
*/
public static void testSystemArrayCopy(int[] arr) {
int[] arr4 = new int[arr.length];
System.arraycopy(arr, 0 , arr4, 0, arr.length);
// int[] arr4 = new int[arr.length-1];
// System.arraycopy(arr, 1 , arr4, 0, arr.length-1);
System.out.println("arr4: " + Arrays.toString(arr4));
System.out.println(arr4 == arr);
System.out.println(Arrays.equals(arr4, arr));
}
/**
* clone()方法拷贝
* @param arr
*/
public static void testClone(int[] arr) {
int[] clone = arr.clone();
System.out.println(clone == arr); // false
System.out.println(clone.equals(arr)); // false
System.out.println(Arrays.equals(clone, arr)); // true
System.out.println(Arrays.toString(clone));
}
public static void testArray2dCopy() {
int[][] arr = new int[][]{{1,2,3},{4,5},{6}};
int[][] newArr = new int[3][];
System.arraycopy(arr, 0, newArr, 0, arr.length);
// 新旧二维数组的内部对象引用是指向同一个地方,所以如果修改数据,新旧数组会同时受到影响
System.out.println(Arrays.toString(arr)); // [[I@1b6d3586, [I@4554617c, [I@74a14482]
System.out.println(Arrays.toString(newArr)); // [[I@1b6d3586, [I@4554617c, [I@74a14482]
arr[0][0] = 100;
System.out.println(Arrays.toString(arr[0]));
System.out.println(Arrays.toString(newArr[0]));
}
}
二维数组拷贝内存结构图分析
/**
* 数组常用排序方法练习
*/
public class ArrayTestSort {
public static void main(String[] args) {
int[] arr = new int[]{3, 5, 8, 2, 1, 6, 9, 4, 7};
testArraysSort(arr);
testSelectSort(arr);
testSelectSortMoreEfficient(arr);
testBubbleSort(arr);
new ArraySortTest().testObjectSort();
}
/**
* 使用Arrays工具类方法,按升序排列数组
* @param arr
*/
public static void testArraysSort(int[] arr) {
Arrays.sort(arr);
System.out.println("ArraysSort:" + Arrays.toString(arr));
}
/**
* 选择排序
* @param arr
*/
public static void testSelectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[i]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("SelectSort: " + Arrays.toString(arr));
}
/**
* 效率更高的方法
* @param arr
*/
public static void testSelectSortMoreEfficient(int[] arr) {
// 效率较高的排序,减少交换次数,每次比较完后只进行一次交换
for (int i = 0; i < arr.length; i++) {
int k = i; // 定义k变量,假设下标为k的值最小
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[k]) {
k = j;
}
}
if (k != i) {
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
// 在for循环外部定义k和temp,这样不用每次在循环时重新在栈上分配空间,只分配一次
int k, temp;
for (int i = 0; i < arr.length; i++) {
k = i;
for (int j = i+1; j < arr.length; j++) {
if (arr[j] < arr[k]) {
k = j;
}
}
if (k != i) {
temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
System.out.println("SelectSortMoreEfficient: " + Arrays.toString(arr));
}
/**
* 冒泡排序
* @param arr
*/
public static void testBubbleSort(int[] arr) {
// 第一种思路
for (int i = arr.length - 1; i >= 1; i--) {
for (int j = 0; j <= i - 1; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
// 另一种思路
for (int i=0; i<arr.length-1; i++){
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] > arr[j+1]){
// int temp = arr[j];
// arr[j] = arr[j+1];
// arr[j+1] = temp;
swap(arr, j);
}
}
}
System.out.println("BubbleSort从小到大:" + Arrays.toString(arr));
// 从大到小排序
for (int i=0; i<arr.length-1; i++){
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] < arr[j+1]){ // 修改一下条件即可
// int temp = arr[j];
// arr[j] = arr[j+1];
// arr[j+1] = temp;
swap(arr, j);
}
}
}
System.out.println("BubbleSort从大到小:" + Arrays.toString(arr));
}
/**
* 对象的排序
*/
public void testObjectSort() {
Person[] people = new Person[5];
people[0] = new Person(160, 1000);
people[1] = new Person(180, 1000);
people[2] = new Person(170, 500);
people[3] = new Person(180, 500);
people[4] = new Person(170, 1500);
for (int i = people.length - 1; i >= 1; i--) {
for (int j = 0; j <= i - 1; j++) {
if (people[j].compare(people[j+1]) < 0) {
Person temp = people[j];
people[j] = people[j+1];
people[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(people));
for (int i = 0; i < people.length; i++) {
System.out.println(people[i]);
}
}
/**
* 交换位置
* @param arr
* @param j
*/
private static void swap(int[] arr, int j) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
class Person {
private int height;
private double money;
Person(int height, double money) {
this.height = height;
this.money = money;
}
/**
* 自定义比较方法
* @param person
* @return 大于返回正数,小于返回负数,等于返回0
*/
public int compare(Person person) {
return height > person.height ? 1
: height < person.height ? -1
: money > person.money ? 1
: money < person.money ? -1
: 0;
}
@Override
public String toString() {
return "Person{" +
"height=" + height +
", money=" + money +
'}';
}
}
}
/**
* 数组练习:数三减一
*/
public class ArrayTestCount3Quit {
public static void main(String[] args) {
testCount3Quit();
testCount3QuitObjectOriented();
}
/**
* 数三减一:普通方法
*/
public static void testCount3Quit() {
// 初始化数组
boolean[] bn = new boolean[100];
for (int i = 0; i < bn.length; i++) {
bn[i] = true;
}
int leftNum = bn.length; // 剩下的个数,剩下最后一个后停止
int index = 0; // 下标标记,数完一圈后重新从0开始
int countNum = 0; // 从0开始数,数到3后重新归零
while (leftNum > 1) {
if (bn[index] == true) {
countNum++;
if (countNum == 3) {
bn[index] = false;
countNum = 0;
leftNum--;
}
}
index++;
if (index == bn.length)
index = 0;
}
for (int i = 0; i < bn.length; i++) {
if (bn[i] == true)
System.out.println(i);
}
}
/**
* 数三减一:采用面向对象思想
*/
public static void testCount3QuitObjectOriented() {
KidCircle kidCircle = new KidCircle(100);
// System.out.println(kidCircle.count);
// for (int i = 0; i < kidCircle.count; i++) {
// System.out.println(kidCircle.kids[i]);
// }
int countNum = 0; // 从0开始数,数到3后重新归零
Kid currentKid = kidCircle.first;
while (kidCircle.count > 1) {
countNum++;
if (countNum == 3) {
kidCircle.delete(currentKid);
countNum = 0;
}
currentKid = currentKid.right;
}
System.out.println(kidCircle.first);
System.out.println(kidCircle.last);
}
}
/**
* 定义小朋友类
*/
class Kid {
int id;
Kid left;
Kid right;
Kid(int id) {
this.id = id;
}
@Override
public String toString() {
return "Kid{" +
"id=" + id +
" left_id=" + left.id +
" right_id=" + right.id +
'}';
}
}
/**
* 定义Kid圆圈类
*/
class KidCircle {
int count = 0; // 当前Kid圈总数
Kid first; // 第一个小朋友
Kid last; // 最后一个小朋友
Kid[] kids;
/**
* 构造函数:构建小朋友手拉手圆圈
* @param total
*/
KidCircle(int total) {
kids = new Kid[total];
for (int i = 0; i < total; i++) {
Kid kid = add(i);
kids[i] = kid;
}
}
/**
* 添加小朋友方法
* @param index
* @return
*/
public Kid add(int index) {
index++;
Kid kid = new Kid(index);
if (index <= 0) {
return null;
} else if (index == 1) {
first = kid;
last = kid;
kid.left = kid;
kid.right = kid;
} else {
kid.left = last;
kid.right = first;
last.right = kid;
first.left = kid;
last = kid;
}
count++;
return kid;
}
/**
* 删除小朋友
* @param kid
*/
public void delete(Kid kid) {
if (count <= 0) {
return;
} else if (count == 1) {
first = last = null;
} else {
kid.left.right = kid.right;
kid.right.left = kid.left;
if (kid == first) {
first = kid.right;
}
if (kid == last) {
last = kid.left;
}
}
count--;
}
@Override
public String toString() {
return "KidCircle{" +
"count=" + count +
", first=" + first +
", last=" + last +
'}';
}
}