JavaSE基础——数组

JavaSE基础——数组

一、初识数组

1. 什么是数组?

前面在学习Java数据类型的时候曾经谈到,Java的数据类型分为基本类型引用类型。基本类型包括整型、浮点型、字符类型、布尔类型四种。引用类型包括类、接口、数组等。

数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是用来存储固定大小的同类型元素。

  • 数组描述的是相同类型数据的有序集合
  • 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
  • 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们

2.声明与创建数组

2.1 声明数组

必须先声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:dataType[] arrayRefVar;

2.2 创建数组

Java语言使用new操作符来创建数组,语法如下:arrayRefVar = new dataType[arraySize];

该语句表明:

  • 使用dataType[arraySize]创建了一个数组;
  • 将新创建的数组赋给变量arrayRefVar;

数组的声明和创建也可以使用一条语句完成:dataType[] arrayRefVar = new dataType[arraySize];

2.3 访问数组

数组内的元素是通过索引访问的,数组索引从0开始

数组的长度可以由arrayRefVar.length得到。

public class Demo01 {
    public static void main(String[] args) {
        int[] nums;         //声明数组,数组名为nums,数组内元素类型为int
        nums = new int[10]; //通过new创建一个数组,数组内元素的个数为10;
        //int[] nums = new int[10];  该语句相当于上述两语句的组合
        //该语句声明并创建一个数组,并初始化数组内各元素的值。
        int [] nums1 = {10, 9, 8, 7, 6, 5};
        //使用 数组名.length 返回数组长度,即数组元素的个数
        System.out.println("数组nums1的长度为:" + nums1.length);
        System.out.println("*******************************");
        //访问数组内元素并赋值,未赋值的元素(比如这里的nums[9])的值默认是对应数据类型的默认值
        //byte、short、int、long、float、double、char的默认值都是0,String的默认值是null
        nums[0] = 1;
        nums[1] = 2;
        nums[2] = 3;
        nums[3] = 4;
        nums[4] = 5;
        nums[5] = 6;
        nums[6] = 7;
        nums[7] = 8;
        nums[8] = 9;
        //遍历数组
        for (int i = 0; i < nums.length; i++) {
            System.out.println("数组内第" + (i + 1) + "个元素的值是:" + nums[i]);
        }
    }
}
----------------------------------
运行结果:
    数组nums1的长度为:6
    *******************************
    数组内第1个元素的值是:1
    数组内第2个元素的值是:2
    数组内第3个元素的值是:3
    数组内第4个元素的值是:4
    数组内第5个元素的值是:5
    数组内第6个元素的值是:6
    数组内第7个元素的值是:7
    数组内第8个元素的值是:8
    数组内第9个元素的值是:9
    数组内第10个元素的值是:0

3. 数组的存储与初始化

3.1 数组的内存简析

img

-

img

  • 声明数组:int[] nums;——在栈内存区开辟一片区域,名为数组名,用于存储数组的地址(数组内第一个元素的地址)
  • 创建数组:nums = new int[10];——在堆内存区开辟连续10个内存单元,用于存储数组元素,名称分别是nums[i]。未赋初值时,内存单元中的值都是对应数据类型的默认值。
  • 通过数组的索引可以访问数组内各元素的值。堆里面开辟了10个连续的内存单元,若要通过数组索引访问之后的内存单元(比如nums[10]),编译器将会报错ArrayIndexOutOfBoundsException,数组下标越界

多维数组内存简析:int[3] [2] 可以看成是3个长度为2的一维数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMZ6akXk-1627611624703)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210730101724246.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6TJCwBV-1627611624705)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210730101939914.png)]

3.2 关于数组是引用类型

通过上面的内存分析可以看到

  • 数组变量是声明数组时产生的,是在栈存储区的。
  • 数组对象(数组本身)是在创建数组时产生的,是在堆存储区的。
  • 因此,数组变量并不是数组本身,在声明和创建数组的时候,是将数组对象的引用赋给了数组变量。因此说,数组是引用类型的数据。也因此可以通过数组变量+索引值访问数组对象。
  • 换句话说。数组变量只是一个引用变量,它可以指向实际的数组对象。数组变量里存储的实际上是数组的地址,也就是数组内第一个元素的地址。通过数组长度设定,编译器会自动判断自第一个元素的地址起多长一段的内存区域是属于该数组的。

3.3 数组初始化

数组初始化实际上是对数组对象初始化,而不是对数组变量初始化。

  1. 静态初始化

静态初始化显式指定每个数组元素的初始值,由系统决定数组的长度(赋了多少个初值数组长度就为多少)。如上面程序中的语句:

//数组的静态初始化1
//该语句创建一个名为nums1的数组,数组内的元素个数为6,数组内元素的值分别是10, 9, 8, 7, 6, 5;
//这种使用花括号的赋值,也称为列表初始化
int [] nums1 = {10, 9, 8, 7, 6, 5};
//数组静态初始化2
int [] nums1 = new int[] {10, 9, 8, 7, 6, 5};
  1. 动态初始化

动态初始化时由程序员指定数组的长度,由系统初始化每个数组元素的默认值。默认是对应数据类型的默认值。后面通过索引修改数组内的元素值。

  1. 默认初始化

数组是引用类型,它的元素相当于类的实例变量。因此数组一旦分配空间,其中每个元素因为按照实例变量同样地方式被隐式初始化。

实际上动态数组初始化时就包含了默认初始化,当声明和创建数组之后,内存中为数组开辟了存储区域,这时数组内存单元中就进行了默认初始化,数组元素的值都是对应数据类型的默认值。

然后通过数组名+数组索引访问数组元素并赋上自定义的值,这就完成了数组的动态初始化。

//动态初始化数组1
int[] nums;       
nums = new int[10]; 
//动态初始化数组2
int[] nums = new int[10];  //该语句相当于上述两语句的组合
//此时已经完成了数组的默认初始化
//通过索引为数组元赋上自定义的值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
//nums[9]未赋自定义的值,因此还是对应数据类型的默认值
//赋上自定义的值后,完成数组的动态初始化

总结

  • 静态初始化显式指定每个数组元素的初始值,由系统决定数组的长度(赋了多少个初值数组长度就为多少)。
  • 动态初始化时由程序员指定数组的长度,由系统初始化每个数组元素的默认值。
  • 不能同时使用动态初始化和静态初始化,即不仅限定了数组元素的个数,而且指定了数组元素的初始值。

4.数组的特点

  • 数组长度是确定的,数组一旦被创建,它的大小不可以改变。若要扩展数组长度,一般是新创建一个对应长度的数组
  • 其中元素必须是相同类型,不允许出现混合类型。且数组内的元素是有序的
  • 数组中的元素可以是任何数据类型,包括基本类型和引用类型。但只能有一种类型
  • 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象,数组对象本身是在堆中
  • 数组下标(索引值)的合法区间是[0,length-1],超出范围就会下标越界报错。ArrayIndexOutOfBoundsException

二、使用数组

2.1 使用for循环遍历数组

//for循环遍历数组
public class Demo02 {
   public static void main(String[] args) {
      double[] myList = {1.9, 2.9, 3.4, 3.5};
      // 打印所有数组元素
      for (int i = 0; i < myList.length; i++) {
         System.out.println(myList[i] + " ");
      }
      System.out.println("**********************");
      // 计算所有元素的总和
      double total = 0;
      for (int i = 0; i < myList.length; i++) {
         total += myList[i];
      }
      System.out.println("Total is " + total);
      System.out.println("**********************");
      // 查找最大元素
      double max = myList[0];
      for (int i = 1; i < myList.length; i++) {
         if (myList[i] > max) max = myList[i];
      }
      System.out.println("Max is " + max);
   }
}
---------------------------------------------------
运行结果:
    1.9 
    2.9 
    3.4 
    3.5 
    **********************
    Total is 11.7
    **********************
    Max is 3.5

2.2 使用增强型for循环(for-each循环)

JDK 1.5 引进了一种新的循环类型,被称为 For-Each 循环或者加强型循环,**它能在不使用下标的情况下遍历数组。**语法格式如下:

for(type element: array)
{
    System.out.println(element);
}
//增强型for循环(for-each循环)
public class Demo01 {
    public static void main(String[] args) {
        double[] myList = {1.9, 2.9, 3.4, 3.5};
        // 打印所有数组元素
        for (double element: myList) {
            System.out.println(element);
        }
    }
}
---------------------------------------------------
运行结果:
    1.9
    2.9
    3.4
    3.5

2.3 数组作为方法的参数

数组也可以作为参数传递给方法。

//数组作为方法的参数
public class Demo01 {
    public static void main(String[] args) {
        //静态初始化数组
        double[] array = {7.2, 63, 3.5, 8.6, 7.9};
        //调用方法,以数组作为方法的参数
        printArray(array);
    }
    //定义方法printArray,以数组作为方法的参数
    public static void printArray(double[] arrays) {
        //使用for-each循环遍历数组
        for (double x:arrays) {
            System.out.print(x + "  ");
        }
    }
}
---------------------------------------------------
运行结果;
    7.2  63.0  3.5  8.6  7.9

2.4 数组作为方法的返回值

数组也可以作为方法的返回值。

//数组作为方法的返回值:反转数组
public class Demo01 {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5};
        //调用方法
        printArray(reverseArray(nums));
    }
    //定义方法reversArray,以数组作为方法的参数,同时也返回数组
    public static int[] reverseArray (int[] arrays) {
        int[] result = new int[arrays.length];
        for (int i = 0, j = arrays.length - 1; i < arrays.length; i++, j-- ) {
            result[j] = arrays[i];
        }
        return result;
    }
    //定义方法printArray,以数组作为方法的参数
    public static void printArray(int[] arrays) {
        //使用for-each循环遍历数组
        for (int x:arrays) {
            System.out.print(x + "  ");
        }
    }
}
---------------------------------------------------
运行结果:
    5  4  3  2  1

三、多维数组

数组内的每个元素可以是任何类型,可以是基本类型也可以是引用类型。那么当数组内的每一个元素都是数组时,就形成了数组的嵌套,这就是多维数组。

以二维数组为例,二维数组内的每一个元素都是一个一维数组。

//多维数组
public class Demo01 {
    public static void main(String[] args) {
        //二维数组静态初始化
        /*
        数组nums有5个元素,每一个元素都一个长度为3的一维数组
        nums[0]: 1 2 3
        nums[1]: 3 2 7
        nums[2]: 7 4 1
        nums[3]: 5 8 3
        nums[4]: 9 2 4
         */
        int[][] nums = { {1,2,3}, {3,2,7}, {7,4,1}, {5,8,3}, {9,2,4} };
        //二维数组的动态初始化:直接为每一维分配空间
        int[][] nums1;
        nums1 = new int[4][2];
        int[][] nums2 = new int[4][2];
        //二维数组动态初始化:分别为每一维分配空间
        //为第一层数组分配空间,即数组内含有两个元素
        String[][] str = new String[2][];
        //为第二层数组分配引用空间,即第一层数组中的第一个元素是含有两个元素的数组,第二个元素是含有三个元素的数组
        str[0] = new String[2];
        str[1] = new String[3];
        //二维数组的动态初始化
        str[0][0] = "Good";
        str[0][1] = "luck";
        str[1][0] = "to";
        str[1][1] = "you";
        str[1][2] = "!";
        //调用打印方法
        printArray(nums);
        System.out.println("**********************");
        printArray(str);
    }
    //创建一个打印二维数组的方法
    public static void printArray(int[][] arrays) {
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                System.out.print(arrays[i][j] + "  ");
            }
            System.out.println();
        }
    }
    public static void printArray(String[][] arrays) {
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                System.out.print(arrays[i][j] + "  ");
            }
            System.out.println();
        }
    }
}
---------------------------------------------------
运行结果:
    1  2  3  
    3  2  7  
    7  4  1  
    5  8  3  
    9  2  4  
    **********************
    Good  luck  
    to  you  !

四、Arrays类

关于数组,Java还提供了一些处理方法,这些方法都在java.util.Arrays类中。这些方法都是静态方法,可以直接调用。利用这些方法可以方便地操作数组,比如:

  • 给数组赋值:通过 fill 方法,将数组内指定范围的元素的值填充为指定值。
  • 对数组排序:通过 sort 方法,将数组按升序排序。
  • 比较数组:通过equals 方法比较数组中元素值是否相等。
  • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。

详细的Array类方法可以查看JDK帮助文档。

4.1 冒泡排序

冒泡排序是最为出名的排序算法之一。

其理论是借助于两层循环,外层循环确定冒泡轮数,内层循环依次比较。因为是循环嵌套,因此时间复杂度是O(n2).

import java.util.Arrays;
//冒泡排序
public class Demo03 {
    public static void main(String[] args) {
        /*
        冒泡排序:
        1.比较数组中两个相邻元素的值,若第一个比第二个值大(小),就交换他们的位置
        2.每一次比较,都会产生出一个最大值(或最小值)
        3.下一轮就可以少进行一次比较
        4.依次循环,直到结束
         */
        int[] nums = {12, 23, 5, 89, 56, 7, 95, 2};
        int[] sort = mpSort(nums);
        //调用Arrays.toString方法输出数组,因为Arrays类里都是静态方法,因此可以直接调用
        System.out.println(Arrays.toString(sort));
    }
    public static int[] mpSort (int[] arrays) {
        //外层确定冒泡的轮数,需要arrays.length - 1轮
        for (int i = 0; i < arrays.length - 1; i++) {
            //内层遍历数组,进行相邻的两个元素的比较,每进行一轮可以少比较一次,arrays.length - 1 - i
            for (int j = 0; j < arrays.length - 1 - i; j++) {
                int temp = 0;
                //若后面的比前面的小,交交换位置,升序排列
                if (arrays[j + 1] < arrays[j]) {
                    temp = arrays[j];
                    arrays[j] = arrays[j + 1];
                    arrays[j + 1] = temp;
                }
            }
        }
        return arrays;
    }
}
---------------------------------------------------
运行结果:
    [2, 5, 7, 12, 23, 56, 89, 95]

4.2 稀疏数组

当一个数组中大部分元素为0,或者为同一值时,存储全部的数组元素比较浪费内存,可以使用稀疏数组来保存数据,压缩空间。

稀疏数组的处理方式:

  • 记录数组有几行几列,有多少不同的值
  • 把不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模。如下图:

img

这样就将一个大的二维数组转变为一个小的二维数组。

  • 稀疏数组中每一个元素都是一个长度为3的数组
  • 稀疏数组的第一个元素中的三个值分别代表原数组的规模和有效值的个数
  • 稀疏数组中后续元素中的三个值分别代表原数组中有效值的位置以及有效值的值
//稀疏数组
public class Demo03 {
    public static void main(String[] args) {
        //定义一个二维数组
        int[][] array = {
                {12,0,0,0,0,0},
                {0,0,0,24,3,0},
                {0,19,0,0,0,0},
                {0,0,0,0,0,89}
        };
        //调用printArray方法打印数组
        System.out.println("打印原二维数组array:"  );
        prtintArray(array);
        System.out.println("******************************");
        //定义一个稀疏数组
        int[][] xarray = {
                {4,6,5},
                {0,0,12},
                {1,3,24},
                {1,4,3},
                {2,1,19},
                {3,5,89}
        };
        //调用printArray方法打印数组
        System.out.println("打印原稀疏数组xarray:" );
        prtintArray(xarray);
        System.out.println("******************************");
        //调用arrToXarr方法将二维数组转换为稀疏数组
        //调用printArray方法打印转换后的稀疏数组
        int[][] nums = arrToXarr(array);
        System.out.println("打印二维数组转为稀疏数组的结果:" );
        prtintArray(nums);
        System.out.println("******************************");
        //调用xarrToArr方法将稀疏数组转换为二维数组
        //调用printArray方法打印转换后的二维数组
        int[][] nums1 = xarrToArr(xarray);
        System.out.println("打印稀疏数组转为二维数组的结果:" );
        prtintArray(nums1);
        System.out.println("******************************");
    }
    //定义arrToXarr方法,作用:将一个二维数组转换为稀疏数组
    public static int[][] arrToXarr(int[][] arrays) {
        //定义一个变量sum,用于记录二维数组中有效值的个数
        int sum = 0;
        //遍历二维数组,记录有效值的个数
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                if (arrays[i][j] != 0) {
                    sum ++;
                }
            }
        }
        //创建一个稀疏数组,稀疏数组的第一层元素的个数就是有效值的个数sum+1;第二层元素的个数必定是3
        int[][] result = new int[sum + 1][3];
        //初始化稀疏数组的第一层元素,分别是二维数组的行数、列数和有效值的个数
        result[0][0] = arrays.length;
        result[0][1] = arrays[0].length;
        result[0][2] = sum;
        //定义一个变量count,用于指向稀疏数组中后续的行元素
        int count = 1;
        //遍历二维数组,搜索有效值
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                if (arrays[i][j] != 0) {
                    //稀疏数组的count行元素记录有效值所处的行数、列数和值
                    result[count][0] = i;
                    result[count][1] = j;
                    result[count][2] = arrays[i][j];
                    count++;
                }
            }
        }
        return result;
    }
    //定义xarrToArr方法,作用:将一个稀疏数组转换为原二维数组
    public static int[][] xarrToArr(int[][] arrays) {
        //定义一个二维数组,数组的行数和列数由稀疏数组第一行元素的前两个值得到
        int[][] result = new int[arrays[0][0]][arrays[0][1]];
        //遍历稀疏数组,稀疏数组后续行元素的值分别代表二维数组有效值所处的行数、列数和值
        for (int i = 1; i < arrays.length; i++) {
            result[arrays[i][0]][arrays[i][1]] = arrays[i][2];
        }
        return result;
    }
    //定义printArray方法,用于遍历并打印二维数组
    public static void prtintArray (int[][] arrays) {
        //使用for-each循环双层嵌套,遍历二维数组
        for (int[] x:arrays) {
            for (int y:x) {
                System.out.print(y + "\t");
            }
            System.out.println();
        }
    }
}
---------------------------------------------------
运行结果:
    打印原二维数组array:
    12    0    0    0    0    0    
    0    0    0    24    3    0    
    0    19    0    0    0    0    
    0    0    0    0    0    89    
    ******************************
    打印原稀疏数组xarray:
    4    6    5    
    0    0    12    
    1    3    24    
    1    4    3    
    2    1    19    
    3    5    89    
    ******************************
    打印二维数组转为稀疏数组的结果:
    4    6    5    
    0    0    12    
    1    3    24    
    1    4    3    
    2    1    19    
    3    5    89    
    ******************************
    打印稀疏数组转为二维数组的结果:
    12    0    0    0    0    0    
    0    0    0    24    3    0    
    0    19    0    0    0    0    
    0    0    0    0    0    89    
    ******************************

转载