Java数组

237 阅读13分钟

初识数组

数组是一种有序的元素序列,用于存储具有相同类型的多个数据元素

  • 数组长度(数据的个数)上限固定,一旦定义,不可更改
  • 数组中的元素可以是任何数据类型,包括基本类型和引用类型

长度属性: 每个数组都具有固定的长度。通过 长度属性 可以获取到数组的长度,语句为:数组名.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

内存分析

屏幕截图 2024-01-24 144850.png


示例: image.png 程序执行流程

  1. main方法进入方法栈执行
  2. 创建数组,JVM会在堆内存中开辟空间,存储数组
  3. 数组在内存中会有自己的内存地址,以十六进制数表示
  4. 数组中有3个元素,默认值为0
  5. JVM将数组的内存地址赋值给引用类型变量array
  6. 变量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() 方法

  1. Arrays.fill(array, value):将整个数组 array 中的所有元素都设置为指定的 value 值。
  2. Arrays.fill(array, fromIndex, toIndex, value):将数组 array 中从索引 fromIndextoIndex - 1 的元素都设置为指定的 value 值。

sort 方法

  1. Arrays.sort(array):对数组中的所有参数进行升序排序
  2. Arrays.sort(array, fromIndex, toIndex):对数组中从索引 fromIndextoIndex-1 的元素进行升序排序

toString方法

Arrays.toString:可以将数组中的元素转换为字符串打印到控制台,并使用方括号 [] 将它们括起来

binarySearch方法

Arrays.binarySearch(array, element):对已升序排序的数组,返回 element 的索引

冒泡排序

冒泡排序是一种简单的排序算法。按照一定的比较规则对数组元素进行排序。

具体步骤

  1. 遍历整个数组,比较相邻的两个元素,如果它们的顺序错误 ,则交换它们的位置
  2. 重复步骤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 的元素及其对应的索引信息,从而节省了存储空间。

微信图片_20240124202655.jpg
//创建一个二维数组 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底层基于动态数组实现,我们可以像遍历数组一样遍历它,同时也可以使用迭代器进行遍历。

三种遍历方式:

  1. 普通for循环
    通过索引访问元素:

    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
        //另一种写法:int element = list.get(i);System.out.println(element);
    }
    
  2. 增强型for循环
    语法更简洁:

    for (int element : list) {
        System.out.println(element);
    }
    
  3. 迭代器遍历
    使用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 等)
  • eelementnewElement:要添加/替换的元素(类型与列表声明一致)
  • index:有效索引位置(0 ≤ index < size())
  • o:要查找/删除的对象(任意类型)
  • collection:包含兼容元素的集合
  • fromIndextoIndex:子列表范围(左闭右开)
  • 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();                         // 列表: []
    }
}