一维数组初始化
数组 一维数组 二维数组 多个数据的组合
数组 引用数据类型 数组元素 数组值 可以是基本数据类型 也可以是引用数据类型
一维数组的声明
元素的数据类型[] 一维数组的名称;
public class ArrayTest1 {
public static void main(String[] args) {
//比如,要存储一个小组的成绩
int[] scores;
int[] grades;
//System.out.println(scores);//未初始化不能使用
//比如,要存储一组字母
char[] letters;
//比如,要存储一组姓名
String[] names;
//比如,要存储一组价格
double[] prices;
}
}
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,…};
或 数据类型[] 数组名;
数组名 = new 数据类型[]{元素1,元素2,元素3,…};
int[] arr = new int[]{1,2,3,4,5};//正确
//或
int[] arr;
arr = new int[]{1,2,3,4,5};//正确
int[] arr = {1,2,3,4,5}
数组动态初始化
数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。
数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或 数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
int[] arr = new int[5];
int[] arr;
arr = new int[5];
// 错误写法
int[] arr = new int[5]{1,2,3,4,5};//错误的,后面有{}指定元素列表,就不需要在[]中指定元素个数了。
数组元素默认初始值
整数型数组元素默认初始化值 0
浮点型数组元素默认初始化值 0.0
字符型数组元素默认初始化值 0
boolean型数组元素默认初始化值 false
引用数据类型数组元素默认初始化值 null
一维数组内存解析
Java内存结构 将内存区域划分为5个区域 程序计数器 虚拟机栈 本地方法栈 堆 方法区
基本数据类型存栈 引用数据类型存堆
数组赋值是浅拷贝
在 Java 中,数组的赋值实际上是进行了浅拷贝(Shallow Copy),而不是深拷贝(Deep Copy)。
当将一个数组变量赋值给另一个数组变量时,实际上是将数组的引用(内存地址)进行了复制。这意味着原始数组和新数组引用同一块内存空间,它们指向相同的对象。
由于数组是引用类型,在进行浅拷贝时,只复制了数组的引用,并没有复制数组中的元素。因此,如果通过一个数组变量修改了数组中的元素,那么另一个数组变量也会反映出这个改变。
int[] array1 = {1, 2, 3};
int[] array2 = array1; // 进行了浅拷贝
array2[0] = 10;
System.out.println(Arrays.toString(array1)); // 输出 [10, 2, 3]
System.out.println(Arrays.toString(array2)); // 输出 [10, 2, 3]
可以看到,修改 array2
中的元素也会影响到 array1
中的元素,因为它们引用同一个数组对象。
如果要实现数组的深拷贝,即创建一个新的数组并复制原始数组中的元素到新数组中,可以使用 Arrays.copyOf()
方法或手动遍历元素进行复制。
int[] array1 = {1, 2, 3};
int[] array2 = Arrays.copyOf(array1, array1.length); // 进行了深拷贝
array2[0] = 10;
System.out.println(Arrays.toString(array1)); // 输出 [1, 2, 3]
System.out.println(Arrays.toString(array2)); // 输出 [10, 2, 3]
通过使用 Arrays.copyOf()
方法,我们创建了一个新的数组 array2
,并将 array1
中的元素复制到了 array2
中。因此,对 array2
的修改不会影响到 array1
。
二维数组的声明推荐
// 元素的数据类型[][] 二维数组的名称;
//存储多组成绩
int[][] grades;
//存储多组姓名
String[][] names;
// int[] x, y[]; x为一维数组 y为二维数组 y 是二维整数数组,其中每个元素都是一维整数数组
// 完整写法 int[][] y
二维数组的初始化
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}}; //声明与初始化必须在一句完成
二维数组动态初始化
int[][] arr = new int[3][2];
String[][] arr = new String[3][4];
// 或者 int[][] arr = new int[3][];
多维数组的遍历展示
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
二维数组本质上是元素类型是一维数组的一维数组
数组之间赋值需维数一样
数组常见算法
均值 总和 最值
数组常见算法 排序优化
特征值计算 数组赋值 复制
public class ArrayCaluate {
public static void main(String[] args) {
/**
* 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,然后求出所有元素的最大值,最小值,总和,平均值,
* 并输出出来。 [10,99] (int)(Math.random() * (b - a + 1)) + a;
*/
int[] arr1 = new int[10];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = (int) (Math.random() * (99 - 10 + 1) + 10);
System.out.print(arr1[i] + "\t");
}
int max = arr1[0];
int min = arr1[0];
int sum = 0;
for (int i = 0; i < arr1.length; i++) {
if (max < arr1[i]) {
max = arr1[i];
}
if (min > arr1[i]) {
min = arr1[i];
}
sum += arr1[i];
}
System.out.println();
System.out.println("最大值: " + max);
System.out.println("最小值: " + min);
double avgValue = sum / arr1.length;
System.out.println("总和: " + sum);
System.out.println("平均值: " + avgValue);
/**
* 评委打分
*
* 分析以下需求,并用代码实现:
*
* (1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7,3
*
* (2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)
*/
int scores[] = {5, 4, 6, 8, 9, 0, 1, 2, 7, 3};
int sum2 = 0;
int max2 = scores[0];
int min2 = scores[0];
for (int i =0; i< scores.length; i++){
if(max2 < scores[i]){
max2 = scores[i];
}
if(min2 > scores[i]){
min2 = scores[i];
}
sum2 += scores[i];
}
System.out.println("选手最终分数 " + (sum2 - max2 - min2) / (scores.length - 2));
}
}
数组赋值与复制区别
数组赋值 浅拷贝 (影响原数组)数组复制 深拷贝
// 数组赋值
int[] arr1;
int[] arr2;
arr1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
arr2 = arr1;
for (int i = 0; i < arr2.length; i++) {
if(i % 2 == 0){
arr2[i] = i;
}
}
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
// 数组复制
int[] arr1;
int[] arr2;
arr1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
arr2 = new int[arr1.length];
for (int i = 0; i < arr2.length; i++) {
arr2[i] = arr1[i];
if(i % 2 == 0){
arr2[i] = i;
}
}
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
数组 反转 扩容 缩容
数组反转
int[] arr = new int[]{34, 54, 3, 2, 65, 7, 34, 5, 76, 34, 67};
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
System.out.println(Arrays.toString(arr));
数组扩容
/**
* 现有数组 int[] arr = new int[]{1,2,3,4,5};
* 现将数组长度扩容1倍,并将10,20,30三个数据添加到arr数组中,如何操作?
*/
int[] arr = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr.length * 2];
for (int i = 0; i < arr.length; i++){
arr2[i] = arr[i];
}
arr2[arr.length] = 10;
arr2[arr.length + 1] = 20;
arr2[arr.length + 2] = 30;
arr = arr2;
System.out.println(Arrays.toString(arr));
数组缩容
/**
* int[] arr3={1,2,3,4,5,6,7}。现需删除数组中索引为4的元素
*/
int[] arr3={1,2,3,4,5,6,7};
int deleteIndex = 4;
for (int i = deleteIndex; i< arr3.length - 1;i++){
arr3[i] = arr3[i+1];
}
arr3[arr3.length-1] = 0;
System.out.println(Arrays.toString(arr3));
int[] arr3={1,2,3,4,5,6,7};
int deleteIndex = 4;
int[] arr4 = new int[arr3.length -1];
for (int i = 0; i< deleteIndex; i++){
arr4[i] = arr3[i];
}
for (int i = deleteIndex; i< arr3.length - 1; i++){
arr4[i] = arr3[i+1];
}
System.out.println(Arrays.toString(arr4));
数组查找 冒泡排序 快速排序
线性查找 算法简单 效率低 复杂度 O(n)
/**
* 线性查找
* int[] arr1 = new int[]{34,54,3,2,65,7,34,5,76,34,67};
* 查找元素5是否在上述数组中出现过?如果出现,输出对应的索引值
*/
int[] arr1 = new int[]{34, 54, 3, 2, 65, 7, 34, 5, 76, 34, 67};
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] == 5) {
System.out.println("对应5的索引值是" + i);
break;
}
}
if(i == arr1.length){
System.out.println("没找到")
}
二分法查找 效率高 复杂度 O(log2N) 数组必须有序
二分法查找需数组有序
/**
* 二分法查找
*
* 定义数组:int[] arr2 = new int[]{2,4,5,8,12,15,19,26,37,49,51,66,89,100};
* 查找元素5是否在上述数组中出现过?如果出现,输出对应的索引值。
* 要求:使用二分法查找,时间复杂度为O(log2^N)
*/
int[] arr2 = new int[]{2, 4, 5, 8, 12, 15, 19, 26, 37, 49, 51, 66, 89, 100};
int target = 5;
int head = 0; // 默认首索引
int end = arr2.length - 1; // 默认尾索引
boolean isFlag2 = false;
while (head <= end) {
// 计算中间索引
int middle = (head + end) / 2;
if (target == arr2[middle]) {
System.out.println("找到元素5,对应的索引值是" + middle);
isFlag2 = true;
break;
} else if (target < arr2[middle]) {
end = middle - 1;
} else if (target > arr2[middle]) {
head = middle + 1;
}
}
if(!isFlag2){
System.out.println("未找到");
}
冒泡排序 O(n^2) 稳定 简单
时间复杂度 空间复杂度 稳定性
相邻两个元素比较 比较
bubbleSort
排序目的快速查找
/**
* 使用冒泡排序,实现整型数组元素的排序操作
* 比如:int[] arr = new int[]{34,54,3,2,65,7,34,5,76,34,67};
*/
int[] arr = new int[]{34, 54, 3, 2, 65, 7, 34, 5, 76, 34, 67};
long start = System.currentTimeMillis();
for (int j = 0; j < arr.length - 1; j++) {
// 执行一轮 找出最大的数
for (int i = 0; i < arr.length - 1 - j; i++) {
if (arr[i] > arr[i + 1]) {
//交换
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
long end = System.currentTimeMillis();
System.out.println("执行时间:" + (end - start) + "ms");
System.out.println(Arrays.toString(arr));
快速排序 O(nlog2N) 快 递归思想
/**
* 快速排序
* 通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,
* 则分别对这两部分继续进行排序,直到整个序列有序。
*
* @author 尚硅谷-宋红康
* @create 13:18
*
*/
public class QuickSort {
public static void main(String[] args) {
int[] data = {9, -16, 30, 23, -30, -49, 25, 21, 30};
System.out.println("排序之前:");
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]+" ");
}
quickSort(data);//调用实现快排的方法
System.out.println("\n排序之后:");
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]+" ");
}
}
public static void quickSort(int[] data) {
subSort(data, 0, data.length - 1);
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
//交换data数组[low]与[high]位置的元素
swap(data, low, high);
} else {
break;
}
}
//交换data数组[start]与[high]位置的元素
swap(data, start, high);
//经过代码[start, high)部分的元素 比[high, end]都小
//通过递归调用,对data数组[start, high-1]部分的元素重复刚才的过程
subSort(data, start, high - 1);
//通过递归调用,对data数组[high+1,end]部分的元素重复刚才的过程
subSort(data, high + 1, end);
}
}
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
Arrays工具类的使用
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:
-
数组元素拼接
-
static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号(“[]”)中。相邻元素用字符 ", "(逗号加空格)分隔。形式为:[元素1,元素2,元素3。。。]
-
static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号(“[]”)中。相邻元素用字符 ", "(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
-
Arrays.toString(arr)
-
-
数组排序
- static void sort(int[] a) :将a数组按照从小到大进行排序 Arrays.sort(a)
- static void sort(int[] a, int fromIndex, int toIndex) : 将a数组的[fromIndex, toIndex)部分按照升序排列 Arrays.sort(a, fromIndex, toIndex);
- static void sort(Object[] a) :根据元素的自然顺序对指定对象数组a按升序进行排序。 Arrays.sort(a) 将对象数组a按倒序进行排列 // 使用自定义的比较器进行降序排序 Arrays.sort(a, Comparator.reverseOrder());
- static void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。 // 使用自定义的比较器进行排序 Arrays.sort(a, new CustomComparator());
-
数组元素的二分查找
- static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
-
数组的复制
- static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组
- static T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组
- static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组
- static T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组
-
比较两个数组是否相等
-
static boolean equals(int[] a1, int[] a2) :比较两个数组的长度、元素 顺序是否完全相同
-
static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
boolean isEquals = Arrays.equals(arr1,arr2);
-
-
填充数组
-
static void fill(int[] a, int val) :用val值填充整个a数组
Arrays.fill(arr, 10) // arr里的元素都替换成10
-
static void fill(Object[] a,Object val):用val对象填充整个a数组
-
static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值
-
static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象*2 位运算 << 1
-
数组常见异常
角标越界异常
访问数组元素 下标超出 [0,数组名.length-1] 范围时就会报越界异常 ArrayIndexOutOfBoundsException。
空指针异常
数组还未分配存储元素的空间 此时访问元素 会报空指针异常 NullPointerException
如何避免空指针异常?
在后端开发中,也有一些类似的方法可以避免空指针异常。以下是一些常用的做法:
- 使用条件判断:在使用对象之前,通过条件判断(如if语句)来检查对象是否为空。例如,在Java中,可以使用类似于
if (obj != null)
的语句进行检查。 - 使用Optional类型:一些编程语言(如Java 8及以上版本)提供了Optional类型,它可以用于封装可能为空的对象,并提供了一系列的操作方法来安全地处理可能为空的情况。使用Optional类型可以避免直接操作空对象引发的空指针异常。
- 合理设计数据结构:在后端开发中,同样需要合理设计数据结构,确保对象在需要时已经被正确初始化,不会出现空指针异常。同时,在使用对象之前,进行必要的校验和处理,确保数据的完整性和有效性。
- 异常处理:在后端代码中,适当地使用异常处理机制来捕获可能导致空指针异常的情况,从而避免程序意外中断。可以使用try-catch语句来捕获异常,并对异常进行相应的处理。
- 日志记录:在后端开发中,合理使用日志记录工具,及时记录可能出现的异常情况,并对异常进行适当的处理和跟踪,以便排查和修复问题。
通过以上方法和编程实践,后端开发人员可以有效地避免空指针异常,并确保代码的稳定性和可靠性。
动态数组
import java.util.ArrayList;
public class PrimeNumbers {
public static void main(String[] args) {
ArrayList<Integer> arr1 = new ArrayList<>(); // 使用 ArrayList 存储质数
int count1 = 0; // 计数质数的变量
for (int i = 2; i <= 10000; i++) {
boolean isPrime = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
isPrime = false;
break; // 如果能被整除,不是质数,跳出内循环
}
}
if (isPrime) {
arr1.add(i); // 将质数添加到 ArrayList 中
count1++;
}
}
// 打印 arr1 和 count1
System.out.println("arr1 = " + arr1); // 打印 ArrayList 内容
System.out.println("count1 = " + count1); // 打印质数的个数
}
}
如果数组 arr1
的大小不确定(即不知道质数的具体个数),可以使用动态数组 ArrayList
来存储质数。ArrayList
在 Java 中可以动态增长,因此可以根据需要添加元素而不需要预先指定大小。之后再说