一维数组

!!! note 目录

数组

一、数组介绍

二、静态一维数组

2.1、初始化静态一维数组

静态初始化一维数组是在创建数组时直接指定数组元素的值,而不是在后续的代码中逐个赋值。Java中有两种方式可以静态初始化一维数组:

  1. 使用new关键字:

    1
    数据类型[] 变量名 = new 数据类型[]{元素1, 元素2, 元素3, ...};

    示例:

    1
    int[] arr = new int[]{1, 2, 3, 34, 45};
  2. 省略new关键字:

    1
    数据类型[] 变量名 = {元素1, 元素2, 元素3, ...};

    示例:

    1
    int[] arr = {1, 2, 3, 34, 45};

这两种方式都会在内存中创建数组对象,并为数组的每个元素赋予特定的值。通常情况下,如果在创建数组对象时就已经知道了数组中应该具体存储哪些元素,就可以使用静态初始化。

三、for each增强for循环

增强for循环,也被称为for-each循环,是 JDK5 中引入的一个语法糖,用于简化数组和集合的遍历操作。其语法结构如下:

1
2
3
for (数据中元素的数据类型 变量名 : 数组名或集合名) {
// 循环体
}

在增强for循环中,变量名代表数组或集合中的每个元素,循环会依次将数组或集合中的每个元素赋给变量名,并执行循环体中的代码块。

增强for循环的优点在于代码简洁,可读性强,能够避免因手动控制下标而引起的错误。但是其缺点是无法直接获取数组或集合的下标,如果需要使用到下标,就需要另外的方式来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ArrayTest02 {
public static void main(String[] args) {
// 静态初始化一维数组
int[] arr = {100, 200, 300};
String[] names = {"jack", "lucy", "tom"};

// 遍历arr数组(for-each)
for(int num : arr){
// num代表数组中的每个元素
System.out.println(num);
}

// 遍历names数组(for-each)
for(String name : names){
System.out.println(name);
}
}
}

四、一维数组动态初始化

动态初始化一维数组是指在创建数组时只指定数组的长度,而不指定数组元素的具体值。Java 中动态初始化一维数组的语法如下:

1
数据类型[] 数组名 = new 数据类型[数组长度];

其中,数据类型 表示数组中元素的数据类型,数组名 是数组的标识符,数组长度 是数组中元素的个数。

例如,要创建一个长度为 5 的整型数组,可以这样写:

1
int[] numbers = new int[5];

这样就创建了一个名为 numbers 的整型数组,它有 5 个元素,但这些元素的值在创建时并未指定,它们会被自动初始化为默认值(对于基本数据类型的数组,例如 int,默认值为 0)。

动态初始化一维数组常用于在后续的代码中根据需要动态设置数组元素的值。

五、方法参数是一维数组时如何传参?

在 Java 中,当一个方法的参数是一个数组时,可以通过以下方式传递参数:

  1. 创建好数组对象,然后将数组对象作为参数传递:

    1
    2
    int[] nums = {1, 2, 3, 4};
    display(nums);
  2. 直接传递数组对象的初始化表达式:

    1
    display(new int[]{1, 2, 3, 4});
  3. 动态初始化数组,并将数组对象作为参数传递:

    1
    display(new int[10]);
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
public class ArrayTest04 {
public static void main(String[] args) {
// 第一种方式(静态初始化):创建好数组对象,然后传进去
int[] nums = {1,2,3,4};
display(nums);

System.out.println("=======================");

// 第二种方式(静态初始化):直接传
//display({1,2,3,4}); // 这是错误的。
display(new int[]{1,2,3,4}); // 这是正确的。注意这个小细节。

System.out.println("=======================");

// 动态初始化方式。
display(new int[10]);
}

/**
* 遍历一维数组。
* @param arr
*/
public static void display(int[] arr) {
for(int num : arr) {
System.out.println(num);
}
}
}

六、 * 引用类型一维数组(+多态)

1
2
3
4
5
6
7
8
package com.camellia.array1.oop1;

/**
* 父类
*/
public class Animal {
}

1
2
3
4
5
6
7
8
9
10
package com.camellia.array1.oop1;
/**
* Bird类
*/
public class Bird extends Animal{
public void fly(){
System.out.println("鸟儿在飞翔!");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
package com.camellia.array1.oop1;

/**
* Cat类
*/
public class Cat extends Animal{

public void catchMouse(){
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
package com.camellia.array1.oop1;

public class Test {
public static void main(String[] args) {

Bird b = new Bird();
Cat c = new Cat();

// 创建一个数组,让该数组既可以存储Cat,又可以存储Bird
// 数组中存储的不是对象本身,实际上是对象在堆内存中的地址。存储的是引用。
Animal[] animals = {b, c, new Cat(), new Bird()};

// 请遍历Animal数组,然后取出的Cat让它抓老鼠,取出的Bird让它飞。
for (Animal animal : animals) {
// 向下转型。
if(animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
} else if(animal instanceof Bird) {
Bird bird = (Bird) animal;
bird.fly();
}
}

// Bird bird = new Bird();
// 编译报错。类型不统一/不一致。
// Cat[] cats = {c, bird};
}
}

七、关于main方法的命令行参数

在 Java 中,public static void main(String[] args) 方法是 Java 应用程序的入口点,它是程序执行的起点。在 main 方法中的 String[] args 参数用于接收命令行参数。

命令行参数是在运行 Java 程序时通过命令行输入的参数,它们可以是任意数量的字符串值,用空格分隔。这些参数被传递给 main 方法的 args 参数,args 是一个字符串数组,其中每个元素对应一个命令行参数。

例如,假设你有一个名为 MyProgram 的 Java 程序,并且在命令行中运行它时提供了一些参数,如下所示:

1
java MyProgram arg1 arg2 arg3

在这种情况下,main 方法的 args 参数将包含一个长度为 3 的字符串数组,其中 args[0]"arg1"args[1]"arg2"args[2]"arg3"

因此,String[] args 参数使得 Java 程序能够接收和处理来自命令行的参数,这样程序可以根据需要执行不同的操作或使用不同的设置。

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
public class ArrayTest05 {
public static void main(String[] args) {

// 需求:使用该系统的用户包括两种
// 一个是普通用户
// 一个是超级管理用户
// 如果是超级管理员用户,在命令行参数的第一个参数上标注:admin,如果是普通用户第一个参数上标注:user
// 假设超级管理员的用户名和密码是:zhangsan 123
// 假设普通用户名和密码是:lisi 123
if (args.length != 3) {
System.out.println("对不起,命令行参数不正确,要使用该系统,命令行参数格式必须是:java ArrayTest 角色 用户名 密码");
return;
}

// 取出角色
String role = args[0];
// 取出用户名
String username = args[1];
// 取出密码
String password = args[2];

// 当两个字符串String进行equals的时候,并且其中有一个字符串是字面量。
// 建议将字面量写到前面。可以避免空指针异常的发生。
if ("admin".equals(role) && username.equals("zhangsan") && password.equals("123")) {
System.out.println("欢迎超级管理员" + username + ",请使用本系统!");
} else if (role.equals("user") && username.equals("lisi") && password.equals("123")) {
System.out.println("欢迎操作员" + username + ",请使用本系统!");
} else {
System.out.println("角色不对,或者用户名不存在,可能密码也错了!");
}

}
}

当两个字符串String进行equals的时候,并且其中有一个字符串是字面量,建议将字面量写到前面。可以避免空指针异常发生。

八、关于方法的可变长度参数

在 Java 中,可变长度参数的语法规则如下:

  1. 语法格式

    1
    2
    3
    返回类型 方法名(数据类型... 参数名) {
    // 方法体
    }
  2. 省略号(...

    • 在方法声明中,省略号(...)用于指示可变长度参数的位置。
    • 省略号必须放在参数类型之后,并且只能出现在参数列表的最后一个位置。
  3. 参数类型

    • 可变长度参数可以是任意数据类型,包括基本数据类型和引用数据类型。
  4. 传递参数

    • 在调用方法时,你可以传递任意数量的参数给可变长度参数。
    • 这些参数将被封装成一个数组传递给方法内部。
  5. 数组访问

    • 在方法内部,可变长度参数被视为一个数组。
    • 你可以像操作数组一样操作可变长度参数,例如使用循环遍历、获取长度等。
  6. 与其他参数共存

    • 可变长度参数可以与其他类型的参数一起使用,但是可变长度参数必须是参数列表的最后一个参数。
  7. 只能有一个可变长度参数

    • 每个方法最多只能有一个可变长度参数。
  • 下面是一个简单的示例,展示了如何在方法中使用可变长度参数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class VarargsExample {

// 使用可变长度参数来计算多个数的和
public static int sum(int... numbers) {
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}

public static void main(String[] args) {
// 调用方法时可以传递任意数量的参数
System.out.println(sum(1, 2, 3)); // 输出:6
System.out.println(sum(1, 2, 3, 4, 5)); // 输出:15
System.out.println(sum(10)); // 输出:10
System.out.println(sum()); // 输出:0
}
}

在上述示例中,sum 方法接受可变长度参数 numbers,并计算这些参数的总和。在 main 方法中,我们展示了如何调用 sum 方法并传递不同数量的参数。

1
2
3
4
5
6
7
8
9
10
11
public class ArrayTest10 {
public static void main(String[] args) {
m5(new Object(), new Object());
}

public static void m5(Object... objs){
for (Object obj : objs){
System.out.println(obj);
}
}
}

九、一维数组扩容

9.1、System.arraycopy()

System.arraycopy() 方法用于将数组中指定范围的元素复制到另一个数组中的指定位置。它的语法如下:

1
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

其中:

  • src:源数组,即要复制的数组。
  • srcPos:源数组中的起始位置。
  • dest:目标数组,即要将元素复制到的数组。
  • destPos:目标数组中的起始位置。
  • length:要复制的元素数量。

9.2、实现一维数组扩容

一维数组扩容通常指的是在已有数组基础上增加额外的存储空间以容纳更多元素。在 Java 中,数组的长度一旦确定后就无法改变,但可以通过创建一个新的更大长度的数组,然后将原数组中的元素复制到新数组中来实现数组扩容的效果。

以下是一种常见的扩容方法:

  1. 创建新数组:创建一个新的数组,长度比原数组大一些,用于存储扩容后的元素。

  2. 复制元素:将原数组中的所有元素复制到新数组中,通常使用 System.arraycopy() 方法或者循环遍历实现。

  3. 更新引用:将原数组的引用指向新数组,这样原数组就被“扩容”了。

Java 中的 ArrayList 就是通过这种方式来实现动态扩容的。当元素数量达到当前数组容量时,ArrayList 会创建一个新的更大的数组,并将原数组中的元素复制到新数组中,然后更新内部引用指向新数组。

以下是简单示例,演示了如何实现一维数组的扩容:

怎么优化?建议减少扩容次数。建议预测数据量,创建一个容量差不多的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
*
*/
public class ArrayExpansion {

public static void main(String[] args) {
int[] oldArray = {1, 2, 3, 4, 5};
int newSize = oldArray.length * 2; // 扩容为原数组长度的两倍
int[] newArray = expandArray(oldArray, newSize);

System.out.println("扩容后的数组:");
for (int num : newArray) {
System.out.print(num + " ");
}
}
public static int[] expandArray(int[] oldArray, int newSize) {
int[] newArray = new int[newSize];
// 复制原数组元素到新数组中
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
return newArray;
}
}