Java语言程序设计:Chap4——数组

数组

概念

  • 数组:若干个相同数据类型按一定顺序排列的集合。
  • 数组中的概念:
    • 数组名:这组相同数据类型的变量,使用的一个统一的名称
    • 下标(索引):区分数组中的每一个数据/变量,我们需要引入索引/编号/下标/下脚标,从0开始,范围是[0,array.length - 1]。越界则会产生ArrayIndexOutOfBoundsException
    • 元素:数组的每一个数据
    • 长度:这组数的总个数,用array.length表示

特点

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
  • 数组中的元素在内存中是依次紧密排列的,有序的。
  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改
  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  • 数组名中引用的是这块连续空间的首地址

分类

1、按照元素类型分:

  • 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
  • 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)

2、按照维度分:

  • 一维数组:存储一组数据
  • 二维数组:存储多组数据,相当于二维表,一行代表一组数据。

一维数组

声明

elementType[] arrayRefVar; //即元素类型[] 数组名称
int[] arr;
double[] arr1;
String[] arr2;
//-------------------------------------------下述不推荐-----------------------------
elementType arrayRefVar[];

数组的声明,需要明确:

  • 数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。

  • 数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。

  • 数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。

声明数组时不能指定长度,如int a[5]是非法的

初始化

静态初始化
  • 如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。

  • 静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。

格式1
elementType[] arrayRefVar = new elementType[]{value1, value2, value3 ...};
//或
elementType[] arrayRefVar;
arrayRefVar = new elementType[]{value1, value2, value3 ...};

new:不同于声明基本数据类型变量,声明一个数组变量时不会给数组分配任何内存空间,它知识创建一个对数组引用的存储位置。如果变量不包含对数组的引用,那么这个变量的值为null。除非数组已经被创建,否则不能给它分配任何元素。声明数组变量之后可以采用new操作符创建数组并将其引用赋给一个变量。

格式2
elementType[] arrayRefVar = {value1, value2, value3, ...}; //不使用操作符new且不能把声明、创建和初始化分开!
//比如下面是错误的
double[] list;
list = {1.1, 2.2, 3.3, ...};
动态初始化
  • 数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。

  • 动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。

elementType[] arrayRefVar = new elementType[arraySize];
//或
elementType[] arrayRefVar;
arrayRefVar = new elementType[arraySize];
  • 长度一旦指定,不可更改!

使用

长度
  • 数组元素的总个数,使用类如arr.length来指明arr的长度。
  • 长度一旦确定就无法更改!
引用
  • 数组存在于一块连续的存储空间,下标从0开始标

  • 使用arr[i]来表示数组中的一个元素

  • i的取值范围是[0, arr.length - 1]

  • []中可以是整型常量或表达式,如arr[i * 2], arr[3]

默认值
  • 数组是引用类型,使用动态初始化方式创建数组时,元素是默认值。
数组元素类型默认值
byte0
short0
int0
long0L
float0.0F
double0.0
char\u0000
booleanfalse
引用类型null
  • 注意:NULL和0不同
遍历
  • 遍历就是把数组的元素一个个拿出来,for循环比较契合这一使用特性。
for循环
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        //输入若干个数
        double[] myList = new double[10];
        Scanner input = new Scanner(System.in);
        System.out.println("Enter 10 numbers: ");
        for (int i = 0; i < myList.length; i++) {
            myList[i] = input.nextDouble();
        }
    }
}
foreach循环
for(elementType element : arrayRefVar) {
    //do_sth
}

不适用下标变量就可以顺序遍历整个数组,上述代码可以认为对arrayRefVar中的每个元素element执行do_sth操作。

当需要以其他顺序遍历数组或改变数组的值时,还是需要使用下标变量。

使用foreach避免了使用for循环可能会导致的两个错误:

  • 下标从1开始
  • 结尾是arrayRefVar.length

内存分析

Java虚拟机的内存划分

在这里插入图片描述

区域名称作用
虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度
的各种基本数据类型、对象引用,方法执行完,自动释放。
堆内存存储对象(包括数组对象),new来创建的,都存储在堆内存。
方法区存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。
本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

数组名中记录的数组的首地址

数组的元素地址计算方式: 首地址 + 每一个元素的宽度 * 下标

  • 以int[] 类型为例,假设首地址0x6666
  • 取第1个元素:0x6666 + 4 * 0 = 0x6666
  • 取第2个元素:0x6666 + 4 * 1 = 0x666a

在这里插入图片描述

一维数组在内存中的存储
public static void main(String[] args) {
  	int[] arr = new int[3];
  	System.out.println(arr);
}

在这里插入图片描述

两个一维数组内存图
  • 两个数组变量,两个数组对象
public static void main(String[] args) {
    int[] arr = new int[3];
    int[] arr2 = new int[2];
    System.out.println(arr);
    System.out.println(arr2);
}

在这里插入图片描述

两个变量指向一个一维数组
  • 两个数组变量,一个数组对象
public static void main(String[] args) {
    // 定义数组,存储3个元素
    int[] arr = new int[3];
    //数组索引进行赋值
    arr[0] = 5;
    arr[1] = 6;
    arr[2] = 7;
    //输出3个索引上的元素值
    System.out.println(arr[0]);
    System.out.println(arr[1]);
    System.out.println(arr[2]);
    //定义数组变量arr2,将arr的地址赋值给arr2
    int[] arr2 = arr;
    arr2[1] = 9;
    System.out.println(arr[1]);
}

在这里插入图片描述

一个数组变量先后指向不同数组对象
public static void main (String[] args) {
    int[] a = {1, 2, 3};
    a = new int[a.length];
    a[0] = 100;
    System.out.println(a[0]);
}

复制数组

上面已经了解了,形如list2 = list1这种不是对数组的复制,复制数组的办法如下:

  1. 使用循环语句逐个复制
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length];
for (int i = 0;i < arr1.length;i ++) {
    arr2[i] = arr1[i];
}
  1. 使用System类中的静态方法arraycopy
int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = new int[arr1.length];
System.arraycopy(arr1, 0, arr2, 0, arr1.length); //arraycopy(arr1, pos1, arr2, pos2, length); pos1和pos2分别表示数组arr1和arr2中的起始位置,复制length个

arraycopy方法没有给目标数组分配空间,使用前必须创建目标数组并分配好空间。复制完成后,arr1和arr2具有相同的内容但位于独立的内存空间

  1. 使用clone方法复制数组

数组传递给方法

public class Main {

    public static void main(String[] args) {
        printArray(new int[]{1, 2, 3, 4, 5, 6});
    }

    public static void printArray(int[] arr) {
        for(int i = 0;i < arr.length;i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

上述代码中new int[]{1,2,3,4,5,6}没有显式的引用变量,这样的数组称为匿名数组。语法为:new elementType[]{value1,value2,…};

Java使用按值传递的方式把实参传递给方法,传递基本数据类型变量和传递数组有很大不同:

  • 传递基本数据类型参数,传递的是实参的
  • 传递数组类型参数,参数值是数组的引用,即方法中的数组和传递的数组是同一个。如果改变方法中的数组,方法外数组也会改变。
public class Main {

    public static void main(String[] args) {
        int[] a = {1,2};
        System.out.println(a[0] + " " + a[1]);
        swap(a[0], a[1]);
        System.out.println(a[0] + " " + a[1]);

        swapFirstTwoInArray(a);
        System.out.println(a[0] + " " + a[1]);
    }

    public static void swap(int x, int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    public static void swapFirstTwoInArray(int[] arr) {
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
}
/* 
输出为:
1 2
1 2
2 1
*/

可见swap没能交换两个元素,但swapFirstTwoInArray方法实现了。因为swap方法的参数为基本数据类型,所以调用swap(a[0], a[1])时,a[0]和a[1]的值传给了方法内部的x和y。x和y的内存位置独立于a[0]和a[1]的内存位置。而swapFirstTwoInArray方法参数是一个数组,数组的引用传给方法。这样,方法外的变量a和方法内的arr都指向在同意内存位置的同一个数组。

方法返回数组
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[] list1 = {1, 2, 3, 4, 5, 6};
        int[] list2 = reverse(list1);
        System.out.println(Arrays.toString(list1));
    }
    public static int[] reverse(int[] list) {
        int[] result = new int[list.length];
        for (int i = 0,j = result.length - 1;i < list.length;i ++,j --) {
            result[j] = list[i];
        }
        return result;
    }
}
可变长参数列表

可以将可变数量相同类型的参数传递给方法,并将其视为数组,声明如下:

typeName... parameterName;

在方法声明中,指定类型后紧跟省略号(…)。只能在方法中指定一个可变长参数,同时该参数必须是最后一个参数。

public class Main {

    public static void main(String[] args) {
        printMax(34, 3, 3, 2, 56.6);
        printMax(new double[]{1, 2, 3});
    }

    public static void printMax(double... numbers) {
        //求数组最大值
        if (numbers.length == 0) {
            System.out.println("Empty array");
            return;
        }

        double result = numbers[0];

        for (int i = 1; i < numbers.length; i++) {
            if (numbers[i] > result) {
                result = numbers[i];
            }
        }

        System.out.println(result);
    }

}

Arrays类

java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:

  • 数组元素拼接
    • static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号(“[]”)中。相邻元素用字符 ", "(逗号加空格)分隔。形式为:[元素1,元素2,元素3。。。]
    • static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号(“[]”)中。相邻元素用字符 ", "(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
  • 数组排序
    • static void sort(int[] a) :将a数组按照从小到大进行排序
    • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列
    • static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。
    • static void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。
  • 数组元素的二分查找
    • static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回-(insectionIndex + 1)。insectionIndex是如果查找失败要插入该元素使得数组保持有序的位置
  • 数组的复制
    • 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[] a, int[] a2) :比较两个数组的长度、元素是否完全相同
    • static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
  • 填充数组
    • static void fill(int[] a, int val) :用val值填充整个a数组
    • 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对象

*命令行参数

二维数组

声明和初始化

声明
elementType[][] arrayRefVar;
静态初始化
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};

定义一个名称为arr的二维数组,二维数组中有三个一维数组

  • 每一个一维数组中具体元素也都已初始化
    • 第一个一维数组 arr[0] = {3,8,2};
    • 第二个一维数组 arr[1] = {2,7};
    • 第三个一维数组 arr[2] = {9,0,1,6};
  • 第三个一维数组的长度表示方式:arr[2].length;
  • 注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。

例子1:

int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成

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

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

arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//错误,静态初始化右边new 数据类型[][]中不能写数字

例子2:

public class TwoDimensionalArrayInitialize {
    public static void main(String[] args) {
        //存储多组成绩
        int[][] grades = {
                    {89,75,99,100},
                    {88,96,78,63,100,86},
                    {56,63,58},
                    {99,66,77,88}
                };

        //存储多组姓名
        String[][] names = {
            {"张三","李四", "王五", "赵六"},
            {"刘备","关羽","张飞","诸葛亮","赵云","马超"},
            {"曹丕","曹植","曹冲"},
            {"孙权","周瑜","鲁肃","黄盖"}
        };
    }
}
动态初始化

如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:

格式1:规则二维表:每一行的列数是相同的

//(1)确定行数和列数
elementType[][] arrayRefVar = new elementType[m][n];
	//其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
	//其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格

//此时创建完数组,行数、列数确定,而且元素也都有默认值

//(2)再为元素赋新值
二维数组名[行下标][列下标] =;

举例:

int[][] arr = new int[3][2];
  • 定义了名称为arr的二维数组

  • 二维数组中有3个一维数组

  • 每一个一维数组中有2个元素

  • 一维数组的名称分别为arr[0], arr[1], arr[2]

  • 给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;

格式2:不规则:每一行的列数不一样

//(1)先确定总行数
elementType[][] arrayRefVar = new elementType[num_rows][];

//此时只是确定了总行数,每一行里面现在是null

//(2)再确定每一行的列数,创建每一行的一维数组
arrayRefVar[行下标] = new elementType[该行的总列数];

//此时已经new完的行的元素就有默认值了,没有new的行还是null

//(3)再为元素赋值
arrayRefVar[行下标][列下标] =;

举例:

int[][] arr = new int[3][];
  • 二维数组中有3个一维数组。
  • 每个一维数组都是默认初始化值null (注意:区别于格式1)
  • 可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
  • 注:int[][]arr = new int[][3]; //非法

获取长度

  • 二维数组的长度/行数:二维数组名.length
  • 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。
  • 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。
  • 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。
public class Test22TwoDimensionalArrayUse {
    public static void main(String[] args){
        //存储3个小组的学员的成绩,分开存储,使用二维数组。
		/*
		int[][] scores1;
		int scores2[][];
		int[] scores3[];*/

        int[][] scores = {
                {85,96,85,75},
                {99,96,74,72,75},
                {52,42,56,75}
        };

        System.out.println(scores);//[[I@15db9742
        System.out.println("一共有" + scores.length +"组成绩.");

        //[[:代表二维数组,I代表元素类型是int
        System.out.println(scores[0]);//[I@6d06d69c
        //[:代表一维数组,I代表元素类型是int
        System.out.println(scores[1]);//[I@7852e922
        System.out.println(scores[2]);//[I@4e25154f
        //System.out.println(scores[3]);//ArrayIndexOutOfBoundsException: 3

        System.out.println("第1组有" + scores[0].length +"个学员.");
        System.out.println("第2组有" + scores[1].length +"个学员.");
        System.out.println("第3组有" + scores[2].length +"个学员.");

        System.out.println("第1组的每一个学员成绩如下:");
        //第一行的元素
        System.out.println(scores[0][0]);//85
        System.out.println(scores[0][1]);//96
        System.out.println(scores[0][2]);//85
        System.out.println(scores[0][3]);//75
        //System.out.println(scores[0][4]);//java.lang.ArrayIndexOutOfBoundsException: 4
    }
}

遍历

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();
}

内存

在这里插入图片描述

多维数组

  • 声明类似于二维数组,继续堆[]即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值