3.1 数组的概述

  • 数组(Array),是多个相同数据类型按一定顺序排列的集合,使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
  • 数组的常见概念
    • 数组名
    • 下标(或索引)
    • 元素
    • 数组的长度
  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间。
  • 数组的长度一旦确定,就不能修改。
  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  • 数组的分类
    • 按照维度:一维数组、二维数组……
    • 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)

3.2 一维数组

3.2.1 一维数组的声明和初始化

  • 数组一旦初始化完成,其长度就确定了。

1、静态初始化

  • 数组的初始化和数组元素的赋值操作同时进行。
  int[] arr1 = new int[]{1, 2, 3, 4};
  int[] arr2 = {1, 2, 3, 4};
  /* 报错
  int[] arr3;
  arr3 = {1, 2, 3, 4};
  */

2、动态初始化

  • 数组的初始化和数组元素的赋值操作分开进行。
  int[] arr3 = new int[4];
  arr3[0] = 1;
  arr3[1] = 2;
  arr3[2] = 3;
  arr3[3] = 4;

3.2.2 如何调用数组的指定位置的元素

  • 通过索引的方式调用。

  • 数组的索引从0开始,到数组的长度-1结束。

  String[] names = {"张三", "李四", "王五", "赵六"};
  String name1 = names[0];
  String name2 = names[1];
  String name3 = names[2];
  String name4 = names[3];

3.2.3 如何修改数组的指定位置的元素

  • 通过索引的方式调用。

  • 数组的索引从0开始,到数组的长度-1结束。

  String[] names = {"张三", "李四", "王五", "赵六"};
  names[0] = "一一";
  names[1] = "二二";
  names[2] = "三三";
  names[3] = "四四";

3.2.4 如何获取数组的长度

  • 属性:length。
  String[] names = {"张三", "李四", "王五", "赵六"};
  int length = names.length;  // 4

3.2.5 如何遍历数组

  • 循环。
  String[] names = {"张三", "李四", "王五", "赵六"};
  // for循环
  for(int i = 0; i < names.length; i++) {
      System.out.println(names[i]);
  }

  // 增强for循环
  for(String name : names) {
      System.out.println(name);
  }

3.2.6 数组元素的默认初始化值

  • 基本数据类型
    • 整型(byte、short、int、long):0
    • 浮点型(float、double):0.0
    • 字符型(char):‘\u0000’
    • 布尔型(boolean):false
  • 引用数据类型:null

3.2.6 数组的内存解析

数组的内存解析

3.2.7 例题

从键盘读入学生成绩,找出最高分,并输出学生成绩等级。

    import java.util.Scanner;
    public class Test {
        public static void main(String[] args) {
            // 1、使用Scanner,读取学生个数
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入学生人数:");
            int num = scan.nextInt();
            // 2、创建数组,存储学生成绩(动态初始化)
            int[] scores = new int[num];
            // 3、给数组中的元素赋值
            System.out.println("请输入" + num + "个学生成绩:");
            for(int i = 0; i < scores.length; i++) {
                scores[i] = scan.nextInt();
            }
            scan.close();
            // 4、获取数组中的元素的最大值(最高分)
            int maxScore = scores[0];
            for(int i = 1; i < scores.length; i++) {
                if(scores[i] > maxScore) {
                    maxScore = scores[i];
                }
            }
            // 5、根据每个学生成绩与最高分的差值,得到每个学生的等级,并输出等级和成绩
            for(int i = 0; i < scores.length; i++) {
                switch((maxScore-scores[i])/10) {
                    case 0:
                        System.out.println("等级:A,成绩:" + scores[i]);
                        break;
                    case 1:
                        System.out.println("等级:B,成绩:" + scores[i]);
                        break;
                    case 2:
                        System.out.println("等级:C,成绩:" + scores[i]);
                        break;
                    default:
                        System.out.println("等级:D,成绩:" + scores[i]);
                }
            }
        }
    }

3.3 数组

  • Java语言里提供了支持***数组的语法。
  • 如果说可以把一维数组当成几何中的线性图形,那么二维数组就相当于是一个表格。
  • 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
  • 其实,从数组底层的运行机制来看,并没有***数组。

3.3.1 二维数组的声明和初始化

1、静态初始化

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

2、动态初始化

    String[][] arr1 = new String[3][2];
    for(int i = 0; i < arr1.length; i++) {
            for(int j = 0; j < arr1[0].length; j++) {
                    arr1[i][j] = "name";
            }
    }

    String[][] arr2 = new String[3][];
    for(int i = 0; i < arr2.length; i++) {
            arr2[i] = new String[2];
    }

3.3.2 如何调用二维数组的指定位置的元素

    String[][] names = new String[]{{"张一","张二","张三"},{"李一","李二","李三"};
    names[0][0];  // "张一"
    names[1][1];  // "李二"

3.3.3 获取数组的长度

    String[][] arr = new String[3][2];
    int len1 = arr.length;  // 3
    int len2 = arr[0].length;  // 2

3.3.4 如何遍历二维数组

    int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
    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 
    7 8 9 
    */

3.3.5 二维数组元素的默认初始化值

    int[][] arr1 = new int[4][3];
    arr1[0];  // 地址
    arr1[0][0];  // 0

    int[][] arr2 = new int[4][];
    arr2[0];  // null
    arr2[0][0];  // 报错:java.lang.NullPointerException

3.3.6 二维数组内存解析

图片说明

3.3.7 练习

  • 练习1

声明:

    int[] x, y[];

在给变量赋值以后,以下选项允许通过编译的是:

    x[0] = y; // ×
    y[0] = x; // √
    y[0][0] = x;  // ×
    x[0][0] = y;  // ×
    y[0][0] = x[0]; // √
    x = y;  // ×
  • 练习2

使用二维数组打印一个10行杨辉三角。

提示:

  1. 第一行有1个元素,第n行有n个元素

  2. 每一行的第一个元素和最后一个元素都是1

  3. 从第三行开始,对于非第一个元素和最后一个元素的元素:yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j]

   import java.util.Arrays;
   public class Test {
       public static void main(String[] args) {
           // 1、声明并初始化二维数组
           int[][] yanghui = new int[10][];
           // 2、给数组元素赋值
           for(int i = 0; i < yanghui.length; i++) {
               yanghui[i] = new int[i+1];
               yanghui[i][0] = 1;
               yanghui[i][i] = 1;
               if(i > 1) {
                   for(int j = 1; j < yanghui[i].length - 1; j++) {
                       yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
                   }
               }
           }
           // 3、遍历二维数组
           for(int[] arr : yanghui) {
               System.out.println(Arrays.toString(arr));
           }
       }
   }
  • 练习3

创建一个长度为6的int型数组,要求数组元素的值都在1~30之间,且是随机赋值。同时,要求元素的值各不相同。

    import java.util.Arrays;

    public class Test {
        public static void main(String[] args) {
            int[] arr = new int[6];
            int num;
            boolean flag;
            for (int i = 0; i < arr.length; i++) {
                flag = true;
                while (true) {
                    num = (int) (Math.random() * 30 + 1);
                    for (int j = 0; j < i; j++) {
                        if(num == arr[j]) {
                            flag = false;
                        }
                    }
                    if(flag) {
                        break;
                    }
                }
                arr[i] = num;
            }
            System.out.println(Arrays.toString(arr));
        }
    }

3.4 数组中涉及到的常见算法

  1. 数组元素的赋值(杨辉三角、回形数等)
  2. 求数值型数组中元素的最大值、最小值、平均数、总和等
  3. 数组的赋值、反转、查找(线性查找、二分查找)
  4. 数组元素的排序算法
  • 练习1

定义一个int型的一维数组,包含10个元素,分别赋予一些随机整数,然后求出所有元素的最大值、最小值、和值、平均值,并输出出来。

要求:所有随机数都是两位数。

    import java.util.Arrays;

    public class Test {
        public static void main(String[] args) {
            int[] arr = new int[10];
            int num;
            for(int i = 0; i < arr.length; i++) {
                num = (int)(Math.random()*90+10);
                arr[i] = num;
            }
            int max = arr[0];
            int min = arr[0];
            int sum = 0;
            int avg;
            for(int i = 0; i < arr.length; i++) {
                if(arr[i] > max) {
                    max = arr[i];
                }
                if(arr[i] < min) {
                    min = arr[i];
                }
                sum += arr[i];
            }
            avg = sum/arr.length;
            System.out.println("数组:" + Arrays.toString(arr));
            System.out.println("最大值:" + max);
            System.out.println("最小值:" + min);
            System.out.println("和值:" + sum);
            System.out.println("平均值:" + avg);
        }
    }
  • 练习2

使用简单数组

  1. 创建两个int型一维数组:arr1、arr2
  2. 使用大括号{},把arr1初始化为8个素数:2,3,5,7,11,13,17,19
  3. 显示arr1的内容
  4. 赋值arr2变量等于arr1,修改arr2中的偶索引元素,使其等于索引值(如arr[0],arr[2]=2),打印出arr1

思考:arr1和arr2是什么关系?——arr1和arr2指向同一个数组地址。

    import java.util.Arrays;

    public class Test {
        public static void main(String[] args) {
            int[] arr1, arr2;
            arr1 = new int[]{2,3,5,7,11,13,17,19};
            for(int i = 0; i < arr1.length; i++) {
                System.out.print(arr1[i] + "\t");
            }
            System.out.println();
            arr2 = arr1;
            for(int i = 0; i < arr2.length; i++) {
                if(i %2 == 0) {
                    arr2[i] = i;
                }
            }
            for(int i = 0; i < arr1.length; i++) {
                System.out.print(arr1[i] + "\t");
            }
            System.out.println();
        }
    }
  • 练习2

实现arr2对arr1数组的复制。

    public class Test {
        public static void main(String[] args) {
            int[] arr1, arr2;
            arr1 = new int[]{2,3,5,7,11,13,17,19};
            for(int i = 0; i < arr1.length; i++) {
                System.out.print(arr1[i] + "\t");
            }
            System.out.println();
            arr2 = new int[arr1.length];
            for (int i = 0; i < arr2.length; i++) {
                arr2[i] = arr1[i];
            }
            for(int i = 0; i < arr2.length; i++) {
                if(i %2 == 0) {
                    arr2[i] = i;
                }
            }
            for(int i = 0; i < arr1.length; i++) {
                System.out.print(arr1[i] + "\t");
            }
            System.out.println();
        }
    }
  • 练习3

数组的复制、反转、查找(线性查找、二分查找)。

    import java.util.Arrays;
    public class Test {
        public static void main(String[] args) {
            String[] arr = new String[]{"AA","BB","CC","DD","EE"};

            // 数组的复制
            String[] arr1 = new String[arr.length];
            for(int i = 0; i < arr1.length; i++) {
                arr1[i] = arr[i];
            }
            System.out.println(Arrays.toString(arr1));

            // 数组的反转
            String[] arr2 = new String[arr.length];
            for(int i = 0; i < arr2.length; i++) {
                arr2[i] = arr[arr.length-1-i];
            }
            System.out.println(Arrays.toString(arr2));

            String temp;
            for(int i = 0; i < arr.length/2; i++) {
                temp = arr[i];
                arr[i] = arr[arr.length-1-i];
                arr[arr.length-1-i] = temp;
            }
            System.out.println(Arrays.toString(arr));

            // 线性查找
            String target = "BB";
            boolean isFlag = true;
            for(int i = 0; i < arr.length; i++) {
                if(target.equals(arr[i])) {
                    System.out.println("找到了指定的元素,位置为:" + i);
                    isFlag = false;
                    break;
                }
            }
            if(isFlag) {
                System.out.println("很遗憾,没有找到!");
            }

            // 二分查找
            int[] nums = new int[]{1,2,3,4,5,6,7,8,9};
            int target2 = 3;
            isFlag = true;
            int left = 0;
            int right = arr.length - 1;
            int mid = (left+right)/2;
            while(left <= right) {
                if(target2 == nums[mid]) {
                    System.out.println("找到了指定的元素,位置为:" + mid);
                    isFlag = false;
                    break;
                } else if(target2 < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            if(isFlag) {
                System.out.println("很遗憾,没有找到!");
            }
        }
    }

3.4.1 排序算法

1、排序

假设含有n个记录的序列为{R1, R2, ……, Rn},其相应的关键字序列为{K1, K2, ……, Kn}。将这些记录重新排列为{Ri1, Ri2, ……, Rin},使得相应的关键字值满足条件:Ki1 ≤ Ki2 ≤ …… ≤ Kin,这样的操作称为排序。

  • 通常来说,排序的目的是快速查找。

2、衡量排序算法的优劣

  1. 时间复杂度:分析关键字的比较次数和记录的移动次数。
  2. 空间复杂度:分析排序算法中需要多少辅助内存。
  3. 稳定性:若两个记录A和B的关键值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。

3、排序算法分类

  • 内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
  • 外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。

4、十大内部排序算法

  • 选择排序
    • 直接选择排序
    • 堆排序
  • 交换排序
    • 冒泡排序
    • 快速排序
  • 插入排序
    • 直接插入排序、折半插入排序、Shell排序
  • 归并排序
  • 桶排序
  • 基数排序

5、算法的5大特征

  1. 输入:有0个或多个输入数据,这些输入必须有清楚的描述和定义。
  2. 输出:至少有1个或多个输出结果,不可以没有输出结果。
  3. 有穷性:算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成。
  4. 确定性:算法中的每一步都是有确定的含义,不会出现二义性。
  5. 可行性:算法的每一步都是清楚且可行的,能让用户用纸笔计算而求出答案。

说明:满足确定性的算法也称为“确定性算法”。现在人们也关注更加广泛的概念。例如:考虑各种非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终止的计算描述,这种描述有时被称为过程(Procedure)。

3.4.2 冒泡排序

    import java.util.Arrays;
    public class Test {
        public static void bubbleSort(int[] arr) {
            int length = arr.length;
            int temp;
            for(int i = 1; i < length; i++) {
                for(int j = 0; j < length - i; j++) {
                    if(arr[j] > arr[j+1]) {
                        temp = arr[j];
                        arr[j] = arr[j+1];
                        arr[j+1] = temp;
                    }
                }
            }
        }
        public static void main(String[] args) {
            int[] arr = new int[]{49,38,65,97,76,13,27,49};
            bubbleSort(arr);
            System.out.println(Arrays.toString(arr));
        }
    }

3.4.3 快速排序

    import java.util.Arrays;
    public class Test {
        public static void quickSort(int[] arr) {
            quickSort(arr, 0, arr.length-1);
        }
        public static void quickSort(int[] arr, int left, int right) {
            int l = left;
            int r = right;
            int pivot = arr[(l+r)/2];
            int temp;
            while(l < r) {
                while(l < r && arr[l] <= pivot) {
                    l++;
                }
                while(l < r && arr[r] >= pivot) {
                    r--;
                }
                if(l < r) {
                    temp = arr[l];
                    arr[l] = arr[r];
                    arr[r] = temp;
                }
            }
            if(l > left) {
                quickSort(arr, left, r-1);
            }
            if(l < right) {
                quickSort(arr, l, right);
            }
        }
        public static void main(String[] args) {
            int[] arr = new int[]{49,38,65,97,76,13,27,49};
            quickSort(arr);
            System.out.println(Arrays.toString(arr));
        }
    }

3.4.4 排序算法性能比较

图片说明

3.5 Arrays工具类

图片说明

    import java.util.Arrays;
    public class Test {
        public static void main(String[] args) {
            // 1、Arrays.equals(int[] a, int[] b):判断两个数组是否相等
            int[] arr1 = new int[]{1,3,2,4};
            int[] arr2 = new int[]{1,3,2,4};
            boolean isEqual = Arrays.equals(arr1, arr2);
            System.out.println(isEqual);

            // 2、Arrays.toString(int[] a):输出数组信息
            System.out.println(Arrays.toString(arr1));

            // 3、Arrays.fill(int[] a, int val):将指定值填充到数组之中
            Arrays.fill(arr1, 10);
            System.out.println(Arrays.toString(arr1));

            // 4、Arrays.sort(int[] a):对数组进行排序
            Arrays.sort(arr2);
            System.out.println(Arrays.toString(arr2));

            // 5、Arrays.binarySearch(int[] a, int key):二分查找
            int[] arr3 = new int[]{-78,46,7,9,2,95,79};
            int index = Arrays.binarySearch(arr3, 9);
            System.out.println(index);
        }
    }

3.6 数组使用中的常见异常

1、java.lang.ArrayIndexOutOfBoundsException

2、java.lang.NullPointerException