程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • JavaSE - 基础篇

    • Java环境搭建
    • Java基础语法
    • Java数据类型
    • Java常量和变量
    • Java进制和存储
    • Java运算符
    • Java流程控制
    • Java数组
      • 1. 数组的概述
        • 1.1 为什么需要数组
        • 1.2 什么是数组
      • 2. 数组的使用
        • 2.1 数组的声明
        • 2.2 数组的创建
        • 2.3 数组的使用和注意事项
      • 4. 数组使用案例
        • 4.1 案例1:创建并打印字母数组
        • 4.2 案例2:查找数组中的最大值及其索引
      • 5. 数组进阶
        • 5.1 数组的赋值机制
        • 5.2 数组拷贝
        • 5.3 数组反转
        • 1. 通过找规律反转
        • 2. 使用逆序赋值方式
        • 3. 创建一个新的数组并逆序赋值
        • 4. 通过两端递进方式反转数组
        • 5.4 数组的扩容机制
        • 1. 介绍
        • 2. 实现步骤
        • 2.1 创建一个新的数组
        • 2.2 将原有数组的元素复制到新数组中
        • 2.3 更新引用,将新数组赋值给原有数组
        • 3.示例代码1
        • 3.示例代码2
      • 6. 数组的排序与查找
      • 7. 多维数组
        • 7.1 静态初始化
        • 7.2 动态初始化
        • 7.3 混合初始化
        • 7.4 访问二维数组
        • 7.5 遍历二维数组
      • 8. 数组练习
        • 1. 生成随机数并保存到数组
        • 2. 倒序打印数组元素
        • 3. 计算平均值
        • 4. 寻找最大值及其索引
        • 5. 查找数组中是否含有特定值
      • 9. 总结
    • Java面向对象上
    • Java面向对象下
    • Java异常机制
    • Java枚举
    • Java反射机制
    • Java代理模式
    • Java泛型
    • Java序列化
    • Java多线程详解
    • Java线程池相关
  • Java
  • JavaSE - 基础篇
scholar
2024-01-17
目录

Java数组

在开始之前,先通过一张图来了解一下我们今天要学习的数组的主要大纲吧

# 1. 数组的概述

# 1.1 为什么需要数组

考虑一个简单的例子:一个养鸡场有多只鸡,需要计算它们的总体重和平均体重。

不使用数组,我们需要为每只鸡分别声明一个变量来存储其体重,然后分别进行求和和计算平均值。这样做在只有几只鸡时看似没问题,但是当鸡的数量增加时,代码将会变得非常冗长且难以维护。

使用数组,我们可以将所有鸡的体重存储在一个数组中,通过循环遍历数组来计算总体重和平均体重,代码如下:

public class ChickenFarm {
    public static void main(String[] args) {
        // 静态初始化数组,存储了多只鸡的体重
        double[] weights = {3, 5, 1, 3.4, 2, 50};
        
        // 初始化总体重变量
        double totalWeight = 0;
        
        // 遍历数组,累加每只鸡的体重
        for (int i = 0; i < weights.length; i++) {
            totalWeight += weights[i];
        }
        
        // 计算平均体重
        double averageWeight = totalWeight / weights.length;
        
        // 输出总体重和平均体重
        System.out.println("总体重=" + totalWeight + "平均体重=" + averageWeight);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1.2 什么是数组

简单来说数组的定义:

数组是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。

数组的特点:

  1. 数组是一组数据的集合。
  2. 长度固定:数组的长度在创建时确定,创建后不可更改。如果需要更大的空间,必须创建一个新的数组。
  3. 同类型元素:数组中的所有元素必须是同一数据类型,这既可以是基本数据类型,也可以是引用数据类型。
  4. 内存连续:数组元素在内存中连续存储,这使得通过索引访问元素非常高效。
  5. 下标从 0 开始:数组中的元素是通过下标来访问的,下标从 0 开始,最大下标为长度减 1,如果数组有 n 个元素,那么数组的索引是从 0 到(n-1)。
  6. 给定数组下标访问下标对应的元素时,时间复杂度为 O(1)。
  7. 数组 是 Java 中的对象,因此可以使用对象属性 lenght 获取到 Array 的长度。
  8. Java 中 数组 都实现了 Cloneable 和 java.io.Serializable 接口。
  9. 数组创建时,其元素会被自动初始化为默认值,数值类型为0,布尔类型为false,引用类型为null。。

数组的分类:

  • 按照维度:一维数组、二维数组、三维数组、…
  • 按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)

# 2. 数组的使用

# 2.1 数组的声明

数组的定义

数据类型[] 数组名  new 数据类型[大小]
int[] a = new int[5];
创建了一个数组,名字为a ,存放了5个int
1
2
3

说明:这是定义数组的一种方法。为了让大家明白,我画了一个数组内存图说明

下载

# 2.2 数组的创建

在Java中,数组是用来存储固定大小的同类型元素的容器。创建数组主要有两种方式:动态初始化和静态初始化。

动态初始化

动态初始化的过程中,我们使用new关键字创建数组,并明确指定数组的长度。数组中的每个元素都会初始化为其数据类型的默认值,比如数值类型(如int、double等)的默认值是0,布尔类型(boolean)的默认值是false,引用类型(如类的实例)的默认值是null。

语法:

数据类型[] 数组名 = new 数据类型[数组长度];
1
int[] intArray = new int[5];
double[] doubleArray = new double[4];
String[] stringArray = new String[3];
1
2
3

静态初始化

静态初始化允许在创建数组的同时直接指定每个元素的值,不需要指定数组长度,因为它会根据提供的元素数量自动确定。静态初始化可以使用完整语法,也可以使用简化形式。

完整语法:

数据类型[] 数组名 = new 数据类型[] {元素1, 元素2, ..., 元素N};
1

简化形式:

数据类型[] 数组名 = {元素1, 元素2, ..., 元素N};
1
// 完整语法
int[] intArray = new int[] {10, 20, 30, 40, 50};

// 简化形式
int[] anotherIntArray = {10, 20, 30, 40, 50};

String[] stringArray = {"Hello", "World", "Java"};
1
2
3
4
5
6
7

# 2.3 数组的使用和注意事项

  1. 类型一致性:数组中的所有元素必须是同一种数据类型,可以是基本数据类型(如int、double、char等)或引用数据类型(如String、数组或任何其他对象类型)。
  2. 默认值:当数组被创建后,其元素会自动初始化为类型的默认值,例如:
    • 数值型数据(int、short、byte、long、float、double)的默认值是0或0.0。
    • char类型的默认值是\u0000(即空字符)。
    • boolean类型的默认值是false。
    • 引用类型(如String或其他对象)的默认值是null。
  • 下标范围:数组的索引从0开始到数组长度-1。尝试访问超出此范围的索引将导致ArrayIndexOutOfBoundsException异常。
  • 数组长度:数组一旦创建,其长度就固定不变。需要更大的数组时,必须创建一个新的数组实例。
  • 引用类型:数组在Java中被视为引用类型。数组变量实际上存储的是对数组对象在内存中位置的引用,而不是数组内容本身。

# 4. 数组使用案例

# 4.1 案例1:创建并打印字母数组

目标:创建一个包含26个元素的char类型数组,依次存放字母'A'到'Z',并使用循环遍历打印每个元素。

  1. 声明和初始化数组:声明一个char类型的数组chars,长度为26。
  2. 填充数组:通过循环使用'A'+i的方式计算每个位置上的字母并赋值给数组。
  3. 打印数组:遍历数组,打印每个元素。这里展示了两种打印方法:一种是遍历打印每个字符,另一种是将字符数组转换为字符串打印。
public class ArrayCase01 {
    public static void main(String[] args) {
        char[] chars = new char[26];
        for (int i = 0; i < chars.length; i++) {
            chars[i] = (char) ('A' + i);
            System.out.print(chars[i] + " ");
        }
        System.out.println("\n初始化的字符数组: " + new String(chars));
    }
}
1
2
3
4
5
6
7
8
9
10

# 4.2 案例2:查找数组中的最大值及其索引

目标:给定一个整型数组,找出数组中的最大值及其对应的索引。

  1. 假设法:先假设数组的第一个元素为最大值,并记录其索引。
  2. 遍历数组:遍历整个数组,如果发现更大的元素,更新最大值及其索引。
  3. 打印结果:打印出找到的最大值及其索引。
public class ArrayCase02 {
    public static void main(String[] args) {
        int[] array = {4, -1, 9, 10, 23};
        int max = array[0]; // 假设第一个元素是最大值
        int maxIndex = 0; // 最大值的索引
        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {
                max = array[i];
                maxIndex = i;
            }
        }
        System.out.println("数组最大值为: " + max + " , 最大值下标为:" + maxIndex);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5. 数组进阶

# 5.1 数组的赋值机制

  1. 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。
int n1 = 2; int n2 = n1;
1
  1. 数组在默认情况下是引用传递,赋的值是地址。

看一个案例,并分析数组赋值的内存图(重点,难点. )。

//代码ArrayAssign.java
int[] arr1 = {1,2,3};

int[] arr2 = arr1;
1
2
3
4

用韩顺平老师的图来讲解以下。

image-20231009141326983

这里的arr1和arr2相当于栈,而数组中的数据{1,2,3}就相当于与内存中的堆,arr1[]是指向{1,2,3}这个堆的,所以可以访问,当把arr1赋值给arr2时,arr2就具有了和arr1一样的访问权,因此对arr2的数据更改,会影响到arr1的数据。

如果还有人听不懂,那我就再举个很简单的例子,我们都玩过“我的世界”这款游戏。比如你和你的朋友一起玩,你的朋友做了一把木剑,放在了他自己的背包里,他又做了一个相同的木剑给了你,你们都有了木剑(这就相当于简单地值拷贝)。现在有一个箱子,你朋友做了一把木剑放在了箱子里,你觉得木剑太垃圾了,把木剑换成了钻石剑放在了箱子里,你朋友打开箱子发现木剑变成了钻石剑(这就是引用赋值)

我想到这里应该讲的也挺清楚的了,数组的赋值引用,变得是地址罢了。希望能够帮助到小伙伴们!!

# 5.2 数组拷贝

数组拷贝是在Java中复制数组元素到一个新的数组对象中的过程,这样原始数组和新数组在内存中占据独立的空间,对其中一个数组的修改不会影响到另一个。下面是实现数组拷贝的步骤和关键点的总结:

  1. 创建源数组:首先定义并初始化源数组arr1,这是将要被拷贝的数组。

  2. 开辟新的数组空间:创建一个新的数组arr2,这个数组的长度与源数组相同,用于存放拷贝过来的数据。这一步是确保数据空间独立的关键。

  3. 元素拷贝:通过循环遍历源数组arr1,将每个元素的值复制到新数组arr2中对应的位置。这一步实现了数据的逐个元素拷贝。

  4. 验证拷贝独立性:为了验证拷贝的正确性和独立性,可以尝试修改新数组arr2的某个元素,然后分别打印两个数组的内容,检查原始数组arr1是否未受影响。

public class ArrayCopy {
    public static void main(String[] args) {
        // 源数组
        int[] arr1 = {10, 20, 30};

        // 创建新数组
        int[] arr2 = new int[arr1.length];

        // 拷贝数据
        for (int i = 0; i < arr1.length; i++) {
            arr2[i] = arr1[i];
        }

        // 修改新数组
        arr2[0] = 100;

        // 打印两个数组验证
        System.out.println("====arr1的元素====");
        for (int i = 0; i < arr1.length; i++) {
            System.out.println(arr1[i]);
        }

        System.out.println("====arr2的元素====");
        for (int i = 0; i < arr2.length; i++) {
            System.out.println(arr2[i]);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

image-20240319002243044

关键点

  • 数据独立性:通过数组拷贝,arr1和arr2在内存中占据独立的空间,修改arr2不会影响到arr1。
  • 手动拷贝:示例中通过手动遍历和赋值的方式完成数组拷贝。这是基础但非常清晰的方法。
  • 自动拷贝方法:Java标准库中提供了数组拷贝的方法,如System.arraycopy()和Arrays.copyOf(),这些方法可以提高拷贝效率,减少代码量。

# 5.3 数组反转

# 1. 通过找规律反转

  1. 声明并初始化数组:首先定义一个数组arr1,并给它赋予初始值。

  2. 计算数组长度:获取数组的长度len,这个长度将用于确定交换操作的次数以及元素的对应关系。

  3. 交换元素:

    • 遍历数组的前半部分(直到中间位置),对于每个位置i,将它与从数组尾部对应的位置len - 1 - i的元素进行交换。
    • 交换操作通过一个临时变量temp来实现,先将一个元素暂存到temp中,然后执行交换,最后将暂存的值赋给目标位置的元素。
  4. 反转完成:经过上述步骤,数组元素的顺序被完全反转。

public class ArrayReverse01 {
    public static void main(String[] args) {
        int[] arr1 = {11, 22, 33, 44, 55, 66}; // 声明并初始化数组
        int temp; // 用于交换的临时变量
        int len = arr1.length; // 获取数组长度
        
        // 遍历数组的一半长度
        for (int i = 0; i < len / 2; i++) {
            // 交换元素
            temp = arr1[i];
            arr1[i] = arr1[len - 1 - i];
            arr1[len - 1 - i] = temp;
        }
        
        // 打印反转后的数组
        System.out.println("反转后的数组: " + Arrays.toString(arr1));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

image-20240319002618838

关键点

  • 遍历一半数组:只需要遍历数组的一半长度,因为每次交换都处理了两个元素。
  • 交换操作:交换操作是反转过程的核心,通过temp变量暂存一个元素的值,实现元素的互换。
  • 适用性广泛:这种方法不依赖于数组的类型,对于整型、字符型等任何类型的数组都适用。
  • 原地反转:不需要额外的数组来存储反转后的结果,节省了内存空间。

# 2. 使用逆序赋值方式

数组的反转还可以通过创建一个新的数组并逆序赋值的方式来实现。这种方法不直接在原数组上进行操作,而是通过将原数组的元素逆序复制到一个新的数组中,从而达到反转的效果,适用于需要保留原数组数据的场景。

  1. 创建新数组:首先创建一个新的数组arrReverse,其大小与原数组arr相同。这个新数组用于存放反转后的元素。

  2. 逆序遍历并赋值:

    • 通过逆序遍历原数组arr(即从最后一个元素开始遍历到第一个元素),同时用另一个循环变量j从0开始递增,将arr中的元素逆序复制到arrReverse中。
    • 这个过程中,arr中索引为i的元素被赋值给arrReverse中索引为j的位置。
  3. 打印反转后的数组:使用Arrays.toString()方法打印新数组arrReverse,展示反转后的结果。

第一种方式:逆序遍历原数组,顺序赋值给新数组。

int[] arr = {11, 22, 33, 44, 55, 66};
int[] arrReverse = new int[arr.length];
for (int i = arr.length - 1, j = 0; i >= 0; i--, j++) {
    arrReverse[j] = arr[i];
}
System.out.println("反转之后的数组2: " + Arrays.toString(arrReverse));
1
2
3
4
5
6

第二种方式:顺序遍历原数组,逆序赋值给新数组。

int[] arr = {11, 22, 33, 44, 55, 66}; 
int[] arrReverseNew = new int[arr.length];
for (int i = 0, j = arr.length - 1; i < arr.length; i++, j--) {
    arrReverseNew[j] = arr[i];
}
System.out.println("反转之后的数组: " + Arrays.toString(arrReverseNew));
1
2
3
4
5
6

关键点

  • 两种遍历方式:两种实现方式展示了不同的遍历和赋值策略,都能达到数组反转的目的。
  • 保留原数组:通过创建新数组来存放反转的元素,原数组arr的内容保持不变。
  • 内存管理:在这种方法中,arr原来指向的数据空间在arr被赋值为新数组后,如果没有其他引用指向原数组,原数组会被Java垃圾回收器回收。

# 3. 创建一个新的数组并逆序赋值

这种方法与前面提到的第二种方法相同,它通过逆序遍历原数组arr,然后将元素顺序赋值到一个新的数组arrReverseNew2中。这种方法实现简单直观,易于理解和操作。

  1. 创建一个新数组arrReverseNew2,其大小与原数组arr相同。
  2. 使用一个for循环逆序遍历原数组arr,同时使用另一个变量j从0开始递增,将arr[i]的值赋给arrReverseNew2[j]。
  3. 打印新数组arrReverseNew2以展示反转后的结果。
int[] arr = {11, 22, 33, 44, 55, 66};
int[] arrReverseNew2 = new int[arr.length];
for (int i = arr.length -1, j = 0; i >= 0; i--, j++) {
    arrReverseNew2[j] = arr[i];
}
System.out.println("反转之后的数组: "+Arrays.toString(arrReverseNew2));
1
2
3
4
5
6

# 4. 通过两端递进方式反转数组

这种方法通过定义两个指针(left和right),分别指向数组的开始和结束。通过while循环,将right指向的元素赋值给left指向的新数组位置,然后同时移动两个指针(left向右移,right向左移),直到left和right相遇或错过,完成数组的反转操作。

  1. 创建新数组:定义一个新的数组arrReverseNew3,其大小与原数组arr相同。
  2. 设置两端指针:定义两个指针left和right,分别初始化为0(数组开始位置)和arr.length - 1(数组结束位置)。
  3. 两端递进交换:通过while循环,只要left < right,就将right位置的元素赋值到新数组的left位置,然后left指针右移,right指针左移。
  4. 完成反转:当left >= right时,结束循环,此时新数组arrReverseNew3中的元素顺序为原数组的逆序。
int[] arr = {11, 22, 33, 44, 55, 66}; 
int[] arrReverseNew3 = new int[arr.length];
int left = 0;
int right = arr.length -1;
while (left < right) {
    arrReverseNew3[left] = arr[right];
    left++;
    right--;
}
// 打印反转后的数组
System.out.println("反转后的数组: " + Arrays.toString(arrReverseNew3));
1
2
3
4
5
6
7
8
9
10
11

# 5.4 数组的扩容机制

# 1. 介绍

在Java中,数组是一种固定长度的数据结构,一旦创建后,其长度无法改变。然而,在实际开发中,经常会遇到需要动态扩容数组的情况。为了解决这个问题,Java提供了一种机制来实现数组的动态扩容,即通过创建一个更大的新数组,然后将原有数组的元素复制到新数组中。

# 2. 实现步骤

步骤 描述
1 创建一个新的数组
2 将原有数组的元素复制到新数组中
3 更新引用,将新数组赋值给原有数组
# 2.1 创建一个新的数组

首先,我们需要创建一个新的数组,用于存放扩容后的元素。根据需要扩容的大小,我们可以使用new关键字创建一个新的数组对象,并指定新数组的长度。例如:

int newSize = oldSize * 2;  // 假设需要将数组扩容为原大小的两倍
int[] newArray = new int[newSize];
1
2
# 2.2 将原有数组的元素复制到新数组中

接下来,我们需要将原有数组的元素复制到新数组中。Java提供了System.arraycopy()方法来进行数组复制操作。该方法需要传入源数组、源数组的起始位置、目标数组、目标数组的起始位置以及要复制的元素数量。代码示例如下:

System.arraycopy(oldArray, 0, newArray, 0, oldSize);
1
# 2.3 更新引用,将新数组赋值给原有数组

最后,我们需要更新引用,将新数组赋值给原有数组。这样,原有数组的引用就指向了新的扩容后的数组,从而完成了数组的扩容操作。代码示例如下:

oldArray = newArray;
1

# 3.示例代码1

下面是一个完整的示例代码,演示了如何实现Java数组的扩容机制:

public class ArrayResizeExample {
    public static void main(String[] args) {
        int[] oldArray = new int[5]; // 初始化原始数组
        int oldSize = oldArray.length;
        int newSize = oldSize * 2; // 计算新数组的大小
        
        // 创建新数组并复制原数组的内容
        int[] newArray = new int[newSize];
        System.arraycopy(oldArray, 0, newArray, 0, oldSize);
        
        // 更新引用
        oldArray = newArray;
        
        System.out.println("原始数组大小:" + oldSize);
        System.out.println("扩容后数组大小:" + newArray.length);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

image-20240319004520463

通过以上示例,我们可以看到原始数组的大小为5,扩容后数组的大小为10,成功实现了数组的扩容操作。

# 3.示例代码2

本示例代码演示了如何在Java中实现数组的动态扩容和元素添加功能。这个过程模拟了在运行时向数组中添加新元素的情况,并且能够根据用户的输入不断扩展数组的大小。以下是实现这一功能的步骤和关键点的总结:

  1. 初始化原始数组:首先定义并初始化一个静态数组arr,这是将要被扩容的原始数组。

  2. 用户输入添加的元素:通过Scanner类创建的对象myScanner,接收用户输入的新元素值。

  3. 创建新数组并扩容:

    • 创建一个长度比原始数组arr多1的新数组arrNew,为了容纳新增的元素。
    • 通过遍历,将原始数组arr的所有元素复制到新数组arrNew中。
    • 将用户输入的新元素值赋值给新数组arrNew的最后一个位置。
  4. 更新原始数组引用:将新数组arrNew的引用赋给原始数组arr的引用。这样,原始数组就指向了新的、包含新增元素的数组。

  5. 循环等待用户输入:使用do-while循环不断地等待用户的输入,询问用户是否继续添加新元素。如果用户选择继续(输入y),则重复上述过程;如果用户选择退出(输入n),则跳出循环。

public class ArrayCopy2 {
	public static void main(String[] args) {
		Scanner myScanner = new Scanner(System.in);
		int[] arr = {1, 2, 3}; // 初始数组

		do {
			// 扩容操作
			int[] arrNew = new int[arr.length + 1];
			for (int i = 0; i < arr.length; i++) {
				arrNew[i] = arr[i];
			}

			// 添加新元素
			System.out.println("请输入你要添加的元素");
			int addNum = myScanner.nextInt();
			arrNew[arrNew.length - 1] = addNum; // 新元素放在数组最后
			arr = arrNew; // 更新原数组引用

			// 输出新数组情况
			System.out.println("====arr扩容后元素情况====");
			for (int i = 0; i < arr.length; i++) {
				System.out.print(arr[i] + "\t");
			}

			// 用户决定是否继续
			System.out.println("\n是否继续添加 y/n");
			char key = myScanner.next().charAt(0);
			if (key == 'n') {
				break; // 退出循环
			}
		} while (true);
		System.out.println("你退出了添加...");
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

关键点总结

  • 动态扩容:通过创建一个新的数组并将原数组的元素以及新添加的元素复制到这个新数组中,实现了数组的动态扩容。
  • 循环添加:利用循环结构,允许用户反复添加新元素,展示了如何根据运行时的需求动态地调整数组的大小。
  • 用户交互:通过Scanner类收集用户输入,实现了与用户的交互,使得用户可以自行决定何时停止添加新元素。

这个示例展示了在不使用Java集合框架的情况下,如何通过数组的基本操作来实现类似动态数组的功能。

# 6. 数组的排序与查找

参考我前面写的十大经典排序算法

# 7. 多维数组

多维数组我们只介绍二维数组。

二维数组的应用场景

比如我们开发一个五子棋游戏,棋盘就是需要二维数组来表示。如图:

image-20231009150512831

二维数组可以视为“数组的数组”,即每个元素本身也是一个数组。这种结构适合存储表格数据或进行坐标映射等操作。

  • 定义与初始化:二维数组可以通过int[][]的方式定义,初始化时可以直接指定每个内部数组的元素,形成一个矩阵。
  • 元素访问:访问二维数组的元素时,需要通过两个索引,即arr[i][j],其中i是外部数组的索引,j是内部数组的索引。

在Java中,二维数组的初始化可以通过几种不同的方式来完成,包括静态初始化和动态初始化。

# 7.1 静态初始化

静态初始化是在声明数组的同时显式指定每个元素的值。适用于在编写代码时已知所有数组元素的情况。

int[][] arr = {
    {0, 0, 0, 0, 0, 0},
    {0, 0, 1, 0, 0, 0},
    {0, 2, 0, 3, 0, 0},
    {0, 0, 0, 0, 0, 0}
};
1
2
3
4
5
6

这种方式直接定义了数组的结构和每个元素的值,构造了一个4行6列的二维数组。

# 7.2 动态初始化

动态初始化涉及在声明数组时不直接指定每个元素的具体值,而是仅指定数组的大小(行数和列数),数组的元素将被初始化为其数据类型的默认值。

指定行数,每行数组独立初始化

可以先指定二维数组的行数,然后对每一行的数组进行独立的动态初始化。

int[][] arr = new int[4][]; // 4行的二维数组
arr[0] = new int[6]; // 第一行有6个元素
arr[1] = new int[6]; // 第二行有6个元素
arr[2] = new int[6]; // 第三行有6个元素
arr[3] = new int[6]; // 第四行有6个元素
1
2
3
4
5

这种方式允许每行数组有不同的长度,即所谓的“不规则二维数组”。

完全动态初始化

当你同时知道行数和列数时,可以一次性地完成二维数组的动态初始化。

int[][] arr = new int[4][6]; // 创建一个4行6列的二维数组
1

这种方式会创建一个4行6列的二维数组,所有元素初始化为int类型的默认值0。

# 7.3 混合初始化

混合初始化结合了静态初始化和动态初始化的特点,对二维数组的某些行使用静态初始化,而对其他行使用动态初始化。

int[][] arr = {
    {1, 2, 3},
    new int[4],
    {4, 5},
    new int[2]
};
1
2
3
4
5
6

这种方式在一些特定场景下非常有用,比如当你知道某些行的具体值,而其他行的值将在程序运行过程中确定。

# 7.4 访问二维数组

  • 获取二维数组的长度(即内部一维数组的数量):
System.out.println("二维数组的元素个数=" + arr.length);
1
  • 访问特定元素,例如获取第3行第4列的元素(注意索引从0开始):
System.out.println("第3个一维数组的第4个值=" + arr[2][3]);
1

# 7.5 遍历二维数组

通过两层for循环遍历二维数组,外层循环遍历行,内层循环遍历列:

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();
}
1
2
3
4
5
6

这种遍历方法可以访问二维数组中的所有元素,并按照矩阵的形式输出数组内容。

# 8. 数组练习

本示例展示了如何使用Java处理数组相关的基础操作,包括生成随机数数组、倒序打印、计算平均值、寻找最大值及其索引、以及查找数组中的特定值。下面是对这个练习的详细总结:

# 1. 生成随机数并保存到数组

  • 创建一个长度为10的整型数组arr。
  • 使用for循环结合Math.random()方法生成1到100之间的随机整数,并将其赋值给数组的每个元素。

# 2. 倒序打印数组元素

  • 通过倒序遍历数组(即从数组的最后一个元素到第一个元素)并打印每个元素,实现数组的倒序输出。

# 3. 计算平均值

  • 遍历数组累加所有元素的值,然后除以数组的长度(元素数量),得到数组元素的平均值。

# 4. 寻找最大值及其索引

  • 初始化max变量为数组的第一个元素,maxIndex为0。
  • 遍历数组,如果发现比max更大的元素,更新max和maxIndex的值。

# 5. 查找数组中是否含有特定值

  • 初始化一个查找标志index为-1(假设未找到)。
  • 遍历数组,如果找到了目标值(如8),更新index为该元素的索引,并退出循环。
  • 根据index的值判断是否找到目标值,并输出相应信息。
// 初始化数组并填充随机数
int[] arr = new int[10];
for(int i = 0; i < arr.length; i++) {
    arr[i] = (int)(Math.random() * 100) + 1;
}

// 倒序打印
for(int i = arr.length - 1; i >= 0; i--) {
    System.out.print(arr[i] + "\t");
}

// 计算平均值和寻找最大值及其索引
double sum = arr[0];
int max = arr[0];
int maxIndex = 0;
for(int i = 1; i < arr.length; i++) {
    sum += arr[i]; 
    if(max < arr[i]) {
        max = arr[i];
        maxIndex = i;
    }
}

// 查找特定值
int findNum = 8;
int index = -1;
for(int i = 0; i < arr.length; i++) {
    if(findNum == arr[i]) {
        System.out.println("找到数" + findNum + " 下标=" + i);
        index = i;
        break;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

这个练习通过一个具体的例子展示了处理数组时的一些基本操作和技巧,如随机数生成、数组遍历、元素比较等,是熟悉数组操作的好例子。通过这类练习,可以加深对数组这一数据结构使用的理解,为处理更复杂的数据结构和算法问题打下坚实的基础。

# 9. 总结

以上便是本文的全部内容,本人才疏学浅,文章有什么错误的地方,欢迎大佬们批评指正!我是scholar,一个在互联网行业的小白,立志成为更好的自己。

如果你想了解更多关于scholar (opens new window) (opens new window),可以关注公众号-书生带你学编程,后面文章会首先同步至公众号。

公众号封面

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
Java流程控制
Java面向对象上

← Java流程控制 Java面向对象上→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式