5. 数组

2023-12-01 11:23:06 浏览数 (1)

1. 数组的概述

1.1 数组的概念

  • 数组(Array) , 是多个相同类型数据按一定顺序排列的集合 , 并使用一个名字命名 , 并通过编号的方式对这些数据进行统一管理
  • 数组中的概念
    • 数组名
    • 下标(索引)
    • 元素
    • 数组的长度

数组的特点 :

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

1.2 数组的分类

  1. 按照元素类型分:
    • 基本数据类型元素的数组 : 每个元素位置存储基本数据类型的值
    • 引用数据类型元素的数组: 每个元素位置存储对象(本质是存储对象的首地址)(在面向对象部分讲解)
  2. 按照维度分:
    • 一维数组 : 存储一组数据
    • 二维数组 :储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。

2. 一维数组的使用

2.1 一维数组的声明

格式:

代码语言:javascript复制
//推荐
元素的数据类型[] 一维数组的名称;

//不推荐
元素的数据类型  一维数组名[];

举例:

代码语言:javascript复制
int[] arr;
int arr1[];
double[] arr2;
String[] arr3;  //引用类型变量数组

数组的声明 , 需要明确:

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

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

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

举例:

代码语言:javascript复制
public class ArrayTest1 {
    public static void main(String[] args) {
        //比如,要存储一个小组的成绩
        int[] scores;
        int grades[];
//        System.out.println(scores);//未初始化不能使用

        //比如,要存储一组字母
        char[] letters;

        //比如,要存储一组姓名
        String[] names;

        //比如,要存储一组价格
        double[] prices;

    }
}

注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法

2.2 一维数组的初始化

静态初始化:

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

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

一维数组声明和静态初始化格式1:

代码语言:javascript复制
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...};

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

new : 关键字 , 创建数组使用的关键字 , 因为数组本身是引用的数据类型 , 所以要用new创建数组实体。

一维数组声明和静态初始化格式2:

代码语言:javascript复制
//必须在一个语句中完成,不能分成两个语句写
数据类型[] 数组名 = {元素1,元素2,元素3...};

动态初始化 :

数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。

动态初始化中 , 只确定了元素的个数(即数组的长度) , 而元素此时还是默认值 , 并未真正的赋值

格式:

代码语言:javascript复制
数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];

或

数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
  • [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
  • 注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。

2.3 一维数组的使用

2.3.1 数组的长度

  • 数组的元素总个数 , 即数组的长度
  • 每个数组都有一个属性length指明它的长度
  • 每个数组都具有长度 , 而且一旦初始化 ,其长度就是确定 , 且是不可变的

2.3.2 数组元素的引用

代码语言:javascript复制
数组名[索引/下标]

数组的下标范围:

Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]

数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];

2.4 一维数组的遍历

将数组中的每个元素分别取出来 , 就是遍历 。 其中 : for 循环与数组的遍历是绝配

举例:

代码语言:javascript复制
public class ArrayTest4 {
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5};
        //打印数组的属性,输出结果是5
        System.out.println("数组的长度:"   arr.length);

        //遍历输出数组中的元素
        System.out.println("数组的元素有:");
        for(int i=0; i<arr.length; i  ){
            System.out.println(arr[i]);
        }
    }
}

2.5 数组元素的默认值

数组是引用类型 , 当使用动态初始化方式创建数组时 , 元素值只是默认值

代码语言:javascript复制
int a[]= new int[5]; 
System.out.println(a[3]); //a[3]的默认值为0

对于引用数据类型而言 , 默认初始化值为: null

3. 一维数组内存分析

3.1 Java虚拟机的内存划分

为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

区域名称

作用

​虚拟机栈​​

用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度 的各种基本数据类型、对象引用,方法执行完,自动释放。​

​堆内存​

存储对象(包括数组对象),new来创建的,都存储在堆内存。

​方法区​

存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。

本地方法栈

当程序中调用了native的本地方法时,本地方法执行期间的内存区域

程序计数器

程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

3.2 一维数组在内存中存储

3.2.1 一个一维数组的内存图

代码语言:javascript复制
public static void main(String[] args) {
  	int[] arr = new int[3];
  	System.out.println(arr);//[I@5f150435
}

3.2.2 数组小标为什么是从0开始的

因为第一个元素距离数组首地址间隔0个单元格。

3.2.3 两个一维数组内存图

其中两个数组相互独立

代码语言:javascript复制
public static void main(String[] args) {
    int[] arr = new int[3];
    int[] arr2 = new int[2];
    System.out.println(arr);
    System.out.println(arr2);
}

3.2.4 两个变量指向一个一维数组

两个数组变量本质上代表同一个数组。

4. 一维数组的应用

案例1:升景坊单间短期出租4个月,550元/月(水电煤公摊,网费35元/月),空调、卫生间、厨房齐全。屋内均是IT行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。

代码语言:javascript复制
public class ArrayTest {
      public static void main(String[] args) {
      int[] arr = new int[]{8,2,1,0,3};
      int[] index = new int[]{2,0,3,2,4,0,1,3,2,3,3};
      String tel = "";
      for(int i = 0;i < index.length;i  ){
            tel  = arr[index[i]];
      }
      System.out.println("联系方式:"   tel);
      }
}

5. 多维数组的使用

5.1 概述

  • 把一维数组当成几何中的现行图形 , 那么二维数组就相当于是一个表格 , 像Excel中的表格 , 围棋棋盘一样

5.2 声明与初始化

5.2.1 声明

二维数组的语法格式:

代码语言:javascript复制
//推荐
元素的数据类型[][] 二维数组的名称;

//不推荐
元素的数据类型  二维数组名[][];
//不推荐
元素的数据类型[]  二维数组名[];

例如:

代码语言:javascript复制
//存储多组成绩
int[][] grades;

//存储多组姓名
String[][] names;

面试:

代码语言:javascript复制
int[] x, y[];
//x是一维数组,y是二维数组

5.2.2 静态初始化

格式:

代码语言:javascript复制
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是二维数组。

5.2.3 动态初始化

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

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

代码语言:javascript复制
//(1)确定行数和列数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
//其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
//其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格

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

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

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

代码语言:javascript复制
//(1)先确定总行数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][];

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

//(2)再确定每一行的列数,创建每一行的一维数组
二维数组名[行下标] = new 元素的数据类型[该行的总列数];

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

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

5.3 数组的长度和角标

  • 维数组的长度/行数:二维数组名.length
  • 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。
  • 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。
  • 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。

5.4 二维数组的遍历

格式:

代码语言:javascript复制
for(int i=0; i<二维数组名.length; i  ){ //二维数组对象.length
    for(int j=0; j<二维数组名[i].length; j  ){//二维数组行对象.length
        System.out.print(二维数组名[i][j]);
    }
    System.out.println();
}

6. 数组的常见算法

6.1 数值型数组特征值统计

特征值涉及到 : 平均值 , 最大值 , 最小值 , 总和等

举例1:数组统计:求总和、均值

代码语言:javascript复制
public class TestArrayElementSum {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9};
        //求总和、均值
        int sum = 0;//因为0加上任何数都不影响结果
        for(int i=0; i<arr.length; i  ){
            sum  = arr[i];
        }
        double avg = (double)sum/arr.length;

        System.out.println("sum = "   sum);
        System.out.println("avg = "   avg);
    }
}

举例2:求数组元素的总乘积

代码语言:javascript复制
public class TestArrayElementMul {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9};

        //求总乘积
        long result = 1;//因为1乘以任何数都不影响结果
        for(int i=0; i<arr.length; i  ){
            result *= arr[i];
        }

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

举例3:求数组元素中偶数的个数

代码语言:javascript复制
public class TestArrayElementEvenCount {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9};
        //统计偶数个数
        int evenCount = 0;
        for(int i=0; i<arr.length; i  ){
            if(arr[i]%2==0){
                evenCount  ;
            }
        }

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

举例4:求数组元素的最大值

代码语言:javascript复制
public class TestArrayMax {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9};
        //找最大值
        int max = arr[0];
        for(int i=1; i<arr.length; i  ){//此处i从1开始,是max不需要与arr[0]再比较一次了
            if(arr[i] > max){
                max = arr[i];
            }
        }

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

举例5:找最值及其第一次出现的下标

代码语言:javascript复制
public class TestMaxIndex {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9};
        //找最大值以及第一个最大值下标
        int max = arr[0];
        int index = 0;
        for(int i=1; i<arr.length; i  ){
            if(arr[i] > max){
                max = arr[i];
                index = i;
            }
        }

        System.out.println("max = "   max);
        System.out.println("index = "   index);
    }
}

举例6:找最值及其所有最值的下标

代码语言:javascript复制
public class Test13AllMaxIndex {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9,9,3};
        //找最大值
        int max = arr[0];
        for(int i=1; i<arr.length; i  ){
            if(arr[i] > max){
                max = arr[i];
            }
        }
        System.out.println("最大值是:"   max);
        System.out.print("最大值的下标有:");

        //遍历数组,看哪些元素和最大值是一样的
        for(int i=0; i<arr.length; i  ){
            if(max == arr[i]){
                System.out.print(i "t");
            }
        }
        System.out.println();
    }
}

优化

代码语言:javascript复制
public class Test13AllMaxIndex2 {
    public static void main(String[] args) {
        int[] arr = {4,5,6,1,9,9,3};
        //找最大值
        int max = arr[0];
        String index = "0";
        for(int i=1; i<arr.length; i  ){
            if(arr[i] > max){
                max = arr[i];
                index = i   "";
            }else if(arr[i] == max){
                index  = ","   i;
            }
        }

        System.out.println("最大值是"   max);
        System.out.println("最大值的下标是["   index "]");
    }
}

举例7(难):输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。** **例如:输入的数组为1, -2, 3, -10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。

代码语言:javascript复制
public class Test5 {
    public static void main(String[] args) {
        int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5};
        int i = getGreatestSum(arr);
        System.out.println(i);
    }
  
    public static int getGreatestSum(int[] arr){
        int greatestSum = 0;
        if(arr == null || arr.length == 0){
            return 0;
        }
        int temp = greatestSum;
        for(int i = 0;i < arr.length;i  ){
            temp  = arr[i];
        
            if(temp < 0){
                temp = 0;
            }
        
            if(temp > greatestSum){
                greatestSum = temp;
            }
        }
        if(greatestSum == 0){
            greatestSum = arr[0];
            for(int i = 1;i < arr.length;i  ){
                if(greatestSum < arr[i]){
                    greatestSum = arr[i];
                }
            }
        }
        return greatestSum;
    }
}

举例8:评委打分

分析以下需求,并用代码实现:

(1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7,3

(2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)

代码语言:javascript复制
/**
 * @author 尚硅谷-宋红康
 * @create 10:03
 */
public class ArrayExer {
    public static void main(String[] args) {
        int[] scores = {5,4,6,8,9,0,1,2,7,3};

        int max = scores[0];
        int min = scores[0];
        int sum = 0;
        for(int i = 0;i < scores.length;i  ){
            if(max < scores[i]){
                max = scores[i];
            }

            if(min > scores[i]){
                min = scores[i];
            }

            sum  = scores[i];
        }

        double avg = (double)(sum - max - min) / (scores.length - 2);

        System.out.println("选手去掉最高分和最低分之后的平均分为:"   avg);
    }
}

6.2 数组元素的赋值与数组复制

6.3 数组元素的反转

6.4 数组的扩容与缩容

6.5 数组元素的查找

6.6 数组元素的排序

7. 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是否存在,如果存在返回第一次找到的下标,不存在返回负数。
  • 数组的复制
    • 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对象

举例:java.util.Arrays类的sort()方法提供了数组元素排序功能:

代码语言:javascript复制
import java.util.Arrays;
public class SortTest {
	public static void main(String[] args) {
		int[] arr = {3, 2, 5, 1, 6};
        System.out.println("排序前"   Arrays.toString(arr));
        Arrays.sort(arr);
        System.out.println("排序后"   Arrays.toString(arr));
	}
}

8. 数组中的常见异常

  1. 数组角标越界异常
  2. 空指针异常

0 人点赞