初识数组
数组是一种有序的元素序列,用于存储具有相同类型的多个数据元素
- 数组长度(数据的个数)上限固定,一旦定义,不可更改
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
长度属性: 每个数组都具有固定的长度。通过 长度属性 可以获取到数组的长度,语句为:数组名.length ,执行结果是数组的长度(int类型结果)
声明数组
数据类型[] 数组名; //首选
数据类型 数组名[]; //效果相同
初始化
初始化数组有动态初始化和静态初始化
动态初始化:在创建数组时,只定义元素的个数,而不为元素赋值
当数组被动态初始化时,未赋值的元素会被自动初始化为对应数据类型的默认值
数据类型[] 数组名 = new 数据类型[数组长度];
数组名[i]=元素i; //赋值
// 可以拆分
数据类型[] 数组名; //先声明
数组名 = new 数据类型[数组长度]; //后初始化
静态初始化:在创建数组时,不定义数据元素个数,而直接将 数组里的数据内容进行赋值,编译器会判定数组有几个元素,所有数据的数据类型必须一致
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…};
// 可以拆分
数据类型[] 数组名; //先声明
数组名 = new 数据类型[]{元素1,元素2,元素3…}; //后初始化
//省略格式
数据类型[] 数组名 = {元素1,元素2,元素3…}; // 可以拆分
访问
数组的内存地址:整个数组在内存中的起始地址。每个数组元素都存储在连续的内存空间中,从该起始地址开始。通过这个地址,可以访问数组中的任何一个元素
索引: 每一个存储到数组的元素,都有一个编号,从0开始,最大的有效索引为数组名.length-1。这个自动编号称为 数组索引 ,可以通过数组的索引访问到数组中的元素。实质上是通过索引来获取对应元素的内存地址,进而访问该元素的值
越界:如果访问数组时的索引超过了有效区间[0,length-1],就会报错ArrayIndexOutOfBoundsException
语法格式
数组名[索引]
访问方式
数组名[索引 i ] = 数值 //为数组中的元素 i 赋值
变量 = 数组名[索引 i ] //获取出数组中的元素 i
内存分析
示例:
程序执行流程
- main方法进入方法栈执行
- 创建数组,JVM会在堆内存中开辟空间,存储数组
- 数组在内存中会有自己的内存地址,以十六进制数表示
- 数组中有3个元素,默认值为0
- JVM将数组的内存地址赋值给引用类型变量array
- 变量array保存的是数组内存中的地址,而不是一个具体数值,因此称为引用数据类型
使用
数组遍历
将数组中的每个元素依次获取出来
for循环遍历
int[] arr = {1, 2, 3, 4, 5};
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
for-each循环遍历(首选)
int[] arr = {1, 2, 3, 4, 5};
for(int num : arr) {
System.out.println(num);
}
数组作为方法参数和返回值
数组作为方法参数
public static void printArray(int[] arr) { //创建方法,接收数组类型的参数
for (int i = 0; i < arr.length; i++) { //进行数组的遍历
System.out.println(arr[i]);
}
}
数组作为返回值
//反转数组元素顺序
public static int[] reverse(int[] arrays){ //返回值是int[]整数数组
int[] result = new int[arrays.length];
for (int i = 0; i < arrays.length; i++) {
result[i] = arrays[arrays.length-i-1]; //将元素按索引值从大到小依次赋值给数组 result
}
return result;
}
public static int[] getArray() { //返回值是int[]整数数组
int[] arr = { 1, 3, 5, 7, 9 };
return arr; //返回数组的地址
}
多维数组
多维数组可以看成数组的数组,比如二维数组就是一个特殊的数组,其每一个元素都是一个一维数组
二维数组初始化
public class Test {
public static void main(String[] args) {
int[][] array1 = {{1,2,3},{4,5,6}}; //静态初始化,为数组的每个元素指定了值
int[][] array3 = new int[][]{{1,2,3},{4,5,6}}; //静态初始化
int[][] array2 = new int[2][3]; //动态初始化
array2[0][0] = 1;
array2[0][1] = 2;
array2[0][2] = 3;
array2[1][0] = 4;
array2[1][1] = 5;
array2[1][2] = 6;
}
}
不定长二维数组的初始化
int[][] array = new int[3][]; //这个数组有3个元素,每个元素都是一个一维数组
array[0] = new int[1]; //给array的第一个元素(即array[0])分配了一个新的长度为1的一维数组
array[1] = new int[2]; //给array的第二个元素(即array[1])分配了一个新的长度为2的一维数组
array[2] = new int[3]; //给array的第三个元素(即array[3])分配了一个新的长度为3的一维数组
因为这些子数组的元素没有被显式初始化,元素值都默认为0
array[0][0] -> 0
array[1][0] -> 0 array[1][1] -> 0
array[2][0] -> 0 array[2][1] -> 0 array[2][2] -> 0
获取二维数组的长度
int length1 = array.length;
int length2 = array[0].length;
System.out.println(length1); // 获取二维数组的第一维长度
System.out.println(length2); // 获取二维数组的第一维的第一个数组长度
Arrays类
Arrays 是 Java 标准库中的一个类,位于 java.util 包中。它提供了用于操作数组的各种静态方法,使用时使用类名进行调用,使数组操作更加方便和高效
fill() 方法
Arrays.fill(array, value):将整个数组array中的所有元素都设置为指定的value值。Arrays.fill(array, fromIndex, toIndex, value):将数组array中从索引fromIndex到toIndex - 1的元素都设置为指定的value值。
sort 方法
Arrays.sort(array):对数组中的所有参数进行升序排序Arrays.sort(array, fromIndex, toIndex):对数组中从索引fromIndex到toIndex-1的元素进行升序排序
toString方法
Arrays.toString:可以将数组中的元素转换为字符串打印到控制台,并使用方括号 [] 将它们括起来
binarySearch方法
Arrays.binarySearch(array, element):对已升序排序的数组,返回 element 的索引
冒泡排序
冒泡排序是一种简单的排序算法。按照一定的比较规则对数组元素进行排序。
具体步骤
- 遍历整个数组,比较相邻的两个元素,如果它们的顺序错误 ,则交换它们的位置
- 重复步骤1,直到整个数组排序完成
两层循环
外层循环:控制整个排序的轮数。需要进行 n-1 轮比较,其中 n 是数组的长度,
内层循环:用于进行实际的比较和交换操作。在每一轮比较中,循环负责遍历未排序部分的相邻元素,并进行比较。如果相邻元素顺序错误,则交换位置。这个过程会一直持续到数组的末尾。第 i 轮比较需要进行n-i次。
//升序排列
public static int[] sort(int[] array){
int temp=0;
for (int i = 0; i < array.length-1; i++) { //外层循环,次数length-1
for (int j = 0; j < array.length-1-i; j++) { //内层循环,进行相邻元素的比较和交换
if(array[j]>array[j+1]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
return array;
}
稀疏数组
稀疏数组是一种特殊的数组存储结构,用于表示大部分元素值为默认值或者为0的数组。
在稀疏数组中,只存储非默认值或非 0 的元素及其对应的索引信息,从而节省了存储空间。
//创建一个二维数组 11*11 0:没有棋子,1:黑棋 2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//输出原始的数组
System.out.println("原始的数组:");
for (int[] array : array1) {
// ↑在每次循环迭代中,array 会被赋值为 array1 的一个子数组(即 array1 的某一行)
for (int i : array) {
// ↑用于遍历当前子数组(由 array 表示)中的所有元素
// ↑在这个循环中,变量 i 会依次被赋值为子数组中的每个元素
System.out.print(i+"\t"); //\t相当于一个或多个空格
}
System.out.println();
// ↑在每次外部 for 循环迭代结束后(打印完一行元素),添加一个换行符
}
// ↓转换为稀疏数组保存
//1.获取有效值的个数
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if(array1[i][j]!=0){
sum++;
}
}
}
//2.创建一个稀疏数组
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;
// ↑初始化稀疏数组的第一行,分别设置其三个列的值为11、11和sum
// ↑这些值提供了关于稀疏数组的一些基本信息
//3.遍历二维数组,将有效值存放到稀疏数组
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if(array1[i][j]!=0){
count++;
array2[count][0] = i;
array2[count][1] = j;
array2[count][2] = array1[i][j];
// ↑如果元素不为0,则将其位置(行i,列j)和值存放到稀疏数组array2中。
// ↑这里使用了变量count来跟踪当前在稀疏数组中的位置
}
}
}
//4.输出稀疏数组
System.out.println("稀疏数组:");
for (int i = 0; i < array2.length; i++) {
for (int j = 0; j < array2[i].length; j++) {
System.out.print(array2[i][j]+"\t");
}
System.out.println();
}
/* 结果:
输出原始的数组
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
稀疏数组 // 行 列 值
11 11 2 // [0] 11 11 2 记录数组有11行11列2个不同元素
1 2 1 // [1] 1 2 1 值1 位于第2行第3列
2 3 2 // [2] 2 3 2 值2 位于第3行第4列
ArrayList
初识ArrayList
基本概念:
ArrayList 是 Java 中实现 List 接口的动态数组类。它可以存储任意类型的对象,并允许在列表任意位置进行元素的插入、删除和获取操作。其核心特性是能够动态调整大小、自动扩展内部数组的容量 以适应存储需求的增长。
- 初始容量为0(无参构造时)
实现原理:
其底层实现基于数组。ArrayList 内部使用一个 Object[] 数组来存储元素。创建 ArrayList 实例时,会初始化一个默认大小的数组。当数组容量不足以容纳新元素时,ArrayList 会自动触发扩容机制:通常会将当前数组容量增加一倍,并将原有元素复制到新数组中。其他操作(如插入、删除、获取)也围绕这个内部数组展开。
1.5倍智能扩容:
ArrayList 采用 1.5倍智能扩容 策略:
- 扩容后容量=当前容量×1.5,舍去小数部分取整
- 扩容触发时机:添加元素时
- 扩容触发条件: 当前元素数 + 1 > 当前容量
- 1.5倍不足时直接使用所需容量
- 存在最大容量限制 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
这个值等于 2,147,483,639(即 2³¹ - 1 - 8)
ArrayList的创建
在Java中,可以通过以下三种方式创建ArrayList实例:
- ArrayList listname = new ArrayList<>():创建 默认初始容量 的空列表。
无参构造默认初始化一个空数组,首次添加元素时才扩容至默认容量10。 - ArrayList<E> listname = new ArrayList<>(int):创建 指定初始容量 的空列表。
若 int > 0,创建大小为 int 的 Object[] 数组;
若 =0,赋值为空数组 EMPTY_ELEMENTDATA;
若 < 0 抛出 IllegalArgumentException。 - ArrayList<E> listname = new ArrayList<>(Collection):基于现有集合Collection 创建包含相同元素的新列表
E:声明ArrayList时指定的元素类型(如ArrayList<String>中的String)。
Collection:集合 Collection 的元素类型必须是 E 或其子类。
三种创建方式示例:
(1)使用默认构造函数创建一个空的 ArrayList:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// 创建一个空的 ArrayList
ArrayList<String> list1 = new ArrayList<>();
// 添加元素到 ArrayList
list1.add("Apple");
list1.add("Banana");
list1.add("Orange");
System.out.println("ArrayList 1: " + list1); // 打印 ArrayList
}
}
//运行结果:ArrayList 1: [Apple, Banana, Orange]
(2)使用带有初始容量的构造函数创建 ArrayList:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// 创建一个具有初始容量的 ArrayList
ArrayList<Integer> list2 = new ArrayList<>(5);
list2.add(10);
list2.add(20);
list2.add(30);
System.out.println("ArrayList 2: " + list2);
}
}
//运行结果:ArrayList 2: [10, 20, 30]
(3)从现有集合创建ArrayList:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// 创建一个包含初始元素的集合
List<String> originalList = new ArrayList<>();
originalList.add("Apple");
originalList.add("Banana");
originalList.add("Orange");
// 使用原始集合创建一个新的 ArrayList
ArrayList<String> newList = new ArrayList<>(originalList);
// 打印新的 ArrayList
System.out.println("New ArrayList: " + newList);
}
}
//运行结果:New ArrayList: [Apple, Banana, Orange]
ArrayList的遍历
由于ArrayList底层基于动态数组实现,我们可以像遍历数组一样遍历它,同时也可以使用迭代器进行遍历。
三种遍历方式:
-
普通for循环
通过索引访问元素:for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); //另一种写法:int element = list.get(i);System.out.println(element); } -
增强型for循环
语法更简洁:for (int element : list) { System.out.println(element); } -
迭代器遍历
使用Iterator接口:Iterator<Integer> it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
ArrayList常用API
ArrayList常用API(操作指南)
| 通用示例代码 | 操作说明 |
|---|---|
| list.add(element); | 在列表末尾添加 element |
| list.add(index, element); | 在 index 处插入 element |
| boolean changed = list.addAll(collection); | 将 collection 所有元素添加到列表 |
| E element = list.get(index); | 获取 index 处的元素 |
| E previousElement = list.set(index, newElement); | 将 index 处元素替换为 newElement |
| E removedElement = list.remove(index); | 移除 index 处元素并返回 |
| boolean found = list.remove(o); | 移除首次出现的 o |
| boolean exists = list.contains(o); | 检查 o 是否存在于列表 |
| int firstIndex = list.indexOf(o); | 返回 o 首次出现的位置索引 |
| int lastIndex = list.lastIndexOf(o); | 返回 o 最后一次出现的位置索引 |
| List<E> sub = list.subList(fromIndex, toIndex); | 获取 [fromIndex, toIndex) 的子列表 |
| list.clear(); | 移除所有元素 |
参数说明:
list:ArrayList实例名称E:列表元素的类型(如 String, Integer 等)e,element,newElement:要添加/替换的元素(类型与列表声明一致)index:有效索引位置(0 ≤ index < size())o:要查找/删除的对象(任意类型)collection:包含兼容元素的集合fromIndex,toIndex:子列表范围(左闭右开)boolean类型后面的变量储存布尔值,找不到参数时为 false
注意:
subList()方法返回的是原列表的一个视图(view),而不是一个独立的副本。这意味着子列表和原列表共享相同的内存数据,对获取的子列表进行修改的时候,原来的列表也会被修改,反之亦然。
实例演示:
import java.util.ArrayList;
import java.util.List;
public class ArrayListAPIDemo {
public static void main(String[] args) {
// 创建ArrayList
ArrayList<String> list = new ArrayList<>();
ArrayList<String> collection = new ArrayList<>();
// 添加元素
list.add("Apple"); // 添加后: [Apple]
list.add(0, "Banana"); // 添加后: [Banana, Apple]
collection.add("Orange");
boolean changed = list.addAll(collection); // changed=true, 列表: [Banana, Apple, Orange]
// 访问和修改元素
String element = list.get(1); // element="Apple"
String previous = list.set(1, "Grape");// previous="Apple", 列表: [Banana, Grape, Orange]
// 删除元素
String removedElement = list.remove(0);// removedElement="Banana", 列表: [Grape, Orange]
boolean found = list.remove("Mango"); // found=false (元素不存在)
// 查询操作
boolean exists = list.contains("Grape"); // exists=true
int firstIndex = list.indexOf("Orange"); // firstIndex=1
list.add("Orange"); // 添加重复元素
int lastIndex = list.lastIndexOf("Orange"); // lastIndex=2
// 子列表操作
List<String> sub = list.subList(0, 2); // sub=[Grape, Orange]
// 清空列表
list.clear(); // 列表: []
}
}