Java|数组

保持对世界的渴望与好奇,而非被浅薄的快乐左右

1. 数组的概述

概述:数组是一种数据结构,用来存储同一类型值的集合。通过一个整形下标(index,或称索引)可以访问数组中的每一个值。

特点:

  • 数组是有序排列的
  • 数组长度一旦确定,不能修改
  • 创建数组对象会在内存中开辟一整块连续的空间
  • 数组属于引用类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型

分类:

  • 按照维度:一维数组、二维数组、...
  • 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组

数据结构:

  • 数据与数据之间的逻辑关系:集合(关系很弱)、一对一(数组里面的元素)、一对多(二叉树)、多对多(社交网络)
  • 数据的存储结构:
    • 线性表:顺序表(比如:数组)、链表、栈、队列、
    • 树形结构:二叉树、树形结构

2. 数组的声明

2.1 Java 虚拟机的内存划分

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

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

注:摘自康师傅 Java 课件与电子教材

2.2 一维数组

在声明数组变量时,需要指定数组类型(数据元素类型紧跟 [] )和数组变量的名称

java 复制代码
// 只声明了变量 a
int[] a;
// 静态初始化:数组的初始化和赋值同时进行
a = new int[]{1001, 1002, 1003, 1004};

// 动态初始化:数组初始化和元素赋值分开操作
String[] names = new String[5];

// Java 中,提供了一种创建数组对象并同时提供初始值的简写形式
// 注意,这个语法中不需要使用 new,甚至不需要指定长度
int[] smallPrimes = {2, 3, 4, 5, 6};

// 也可以使用下面两种形式定义一个数组变量,第一种风格使用的多
// 因为它可以将类型 int[](整形数组) 与变量明清晰的分开
int[] b;
int c[];

错误的方式

java 复制代码
//int[] arr1 = new int[];
//int[5] arr2 = new int[5];
//int[] arr3 = new int[3]{1,2,3};

数组元素调用、获取数组 length、两种方式遍历数组元素

java 复制代码
public class ArrayTest1 {
    public static void main(String[] args) {
        // 动态初始化:数组初始化和元素赋值分开操作
        String[] names = new String[5];

        // 通过角标的方式调用
        names[0] = "张三";
        names[1] = "李四";
        names[2] = "王五";
        names[3] = "赵六";
        names[4] = "孙七";

        // 获取数组元素的长度:length
        System.out.println(names.length);

        // 遍历数组 for 循环
        for (int i = 0; i < names.length; i++) {
            System.out.print(names[i] + " ");
        }
        // 换行一下
        System.out.println();

        // 通过 for each 循环进行遍历数组
        for (String element : names) {
            System.out.print(element + " ");
        }
    }

}

默认初始化值

  • 数组元素是整型:0
  • 数组元素是浮点型:0.0
  • 数组元素是char型:0或'\u0000',而非'0'
  • 数组元素是boolean型:false
  • 数组元素是引用数据类型:null

一维数组内存解析大致过程

2.3 二维数组

二维数组元素获取、获取数组 length、两种方式遍历数组元素

java 复制代码
public class ArrayTest2 {
    public static void main(String[] args) {
        // 静态初始化
        int[][] arr1 = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {22, 12, 9}};

        // 动态初始化1
        String[][] arr2 = new String[3][2];
        // 动态初始化2
        String[][] arr3 = new String[3][];

        //也是正确的写法:
        int arr4[][] = new int[][]{{1, 2, 3}, {4, 5, 9, 10}, {6, 7, 8}};
        int[] arr5[] = new int[][]{{1, 2, 3}, {4, 5, 9, 10}, {6, 7, 8}};
        int[][] arr6 = {{1, 2, 3}, {4, 5}, {6, 7, 8}};//类型推断

        // 获取数组指定位置的元素
        System.out.println(arr1[0][2]); // 3
        // System.out.println(arr1[0][3]); // ArrayIndexOutOfBoundsException 数组索引越界异常
        System.out.println(arr2[1][1]); // null
        // System.out.println(arr3[1][1]); // NullPointerException 空指针异常

        // 数组长度
        System.out.println(arr1.length); // 4
        System.out.println(arr1[1].length); // 3

        // 遍历二维数组
        for (int i = 0; i < arr1.length; i++) {
            for (int j = 0; j < arr1[i].length; j++) {
                System.out.print(arr1[i][j] + " ");
            }
            System.out.println();
        }
        
        // 换行
        System.out.println();

        for (int[] e : arr1) {
            for (int j : e) {
                System.out.print(j + " ");
            }
            System.out.println();
        }
    }
}

错误的方式

java 复制代码
//String[][] arr4 = new String[][4];
//String[4][3] arr5 = new String[][];
//int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};

数组元素的默认初始化值

  • 针对于初始化方式一:比如:int[][] arr = newint[4][3];

    • 外层元素的初始化值为:地址值
    • 内层元素的初始化值为:与一维数组初始化情况相同
  • 针对于初始化方式二:比如:int[][] arr = newint[4][];

    • 外层元素的初始化值为:null
    • 内层元素的初始化值为:不能调用,否则报错

二维数组内存解析大致过程

3. 数组的常见算法

3.1 数组元素的赋值

杨辉三角

java 复制代码
public class ArrayEver {
    public static void main(String[] args) {
        // 1. 新建二维数组,动态初始化
        int[][] yangHui = new int[10][];
        // 2. 为二维数组赋值操作
        for (int i = 0; i < yangHui.length; i++) {
            // 第一行有 1 个元素,总共 10 行
            yangHui[i] = new int[i + 1];

            // 2.1 为首末行元素赋值 1
            yangHui[i][0] = yangHui[i][i] = 1;

            // 2.2 给每行的非首末元素赋值操作
            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[] ints : yangHui) {
            for (int anInt : ints) {
                System.out.print(anInt + " ");
            }
            System.out.println();
        }
        /*
        1 
        1 1 
        1 2 1 
        1 3 3 1 
        1 4 6 4 1 
        1 5 10 10 5 1 
        1 6 15 20 15 6 1 
        1 7 21 35 35 21 7 1 
        1 8 28 56 70 56 28 8 1 
        1 9 36 84 126 126 84 36 9 1 
        */
    }
}

3.2 针对数值型的数组

java 复制代码
/**
 * 针对数值类型的基本操作
 * 定义一个 int 型的一维数组,包含 10 个元素,分别赋值一些随时数,所有随机数都是两位数
 * 求:数值型数组元素的最大值、最小值、平均数、总和
 * [10, 99] 公式:(int)(Math.random() * (99 - 10 + 1) + 10)
 * 
 */
public class ArrayEver1 {
    public static void main(String[] args) {
        int[] arr = new int[10];
        // 数组赋值操作
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * (99 - 10 + 1) + 10);
        }

        // 遍历
        for (int i : arr) {
            System.out.print(i + " ");
        }

        // 换行
        System.out.println();

        // 求数组元素的最大值
        int maxValue = arr[0];
        for (int i : arr) {
            if(maxValue < i){
                maxValue = i;
            }
        }
        System.out.println("最大值:" + maxValue);

        // 求数组元素的最小值
        int minValue = arr[0];
        for (int i : arr) {
            if(maxValue > i){
                maxValue = i;
            }
        }
        System.out.println("最小值:" + minValue);

        // 求数组元素的总和
        double sum = 0;
        for (int i : arr) {
            sum += i;
        }
        System.out.println("总和:" + sum);

        // 求数组元素的平均数
        double avgValue = sum / arr.length;
        System.out.println("平均数:" + avgValue);
    }
}

Math.random()方法

在 Java 中主要提供了两种方式产生随机数,分别为调用 Math 类的 random()方法Random 类提供的产生各种数据类型随机数的方法。

在 Math 类中存在一个 random() 方法,用于产生随机数字,这个方法默认生成大于等于 0.0 且小于 1.0 的 double 型随机数;即 0 <= Math.random() < 1.0,虽然 Math.random() 方法只可以产生 0 ~ 1 之间的double 型数字,其实只要在 Math.random() 语句上稍加处理,就可以产生任意范围的随机数。

(int)(Math.Random() * n ) → 返回大于等于 0 且小于 n 的随机数

m + (int)(Math.Random() * n ) → 返回大于等于 m 且小于 m+n(不包括m+n)的随机数

使用 Math 类的 random() 方法也可以随机生成字符,可以使用如下代码生成 a ~ z 之间的字符。

(char)('a' + Math.random() * ('z' - 'a' + 1));

上述表达式可以求出更多的随机字符,如 A ~ Z 之间的随机字符,进而推理出求任意字符之间的随机字符,可以使用下列语句表示。

(char)(char1 + Math.random() * (cha2 - cha1 + 1));

3.3 数组的赋值与复制

赋值

java 复制代码
public class ArrayTest3 {
    public static void main(String[] args) {
        int[] array1, array2;
        array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
        // array1 和 array2 地址相同,都指向了堆空间同一个数组实体
        array2 = array1;
        for (int i = 0; i < array2.length; i++) {
            if (i % 2 == 0) {
                array2[i] = i;
            }
        }
        // 打印结果相同
        for (int i : array1) System.out.print(i + " "); // 0 3 2 7 4 13 6 19 
        System.out.println();
        for (int i : array2) System.out.print(i + " "); // 0 3 2 7 4 13 6 19 
    }
}

简单理解如下图

复制,目前复制 Java 数组的复制,可以使用 System.arraycopy() 方法,但在这里,先用最简单的方式描述

java 复制代码
public class ArrayTest4 {
    public static void main(String[] args) {
        int[] array1, array2;
        array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
        // 数组的复制
        array2 = new int[array1.length];
        for (int i = 0; i < array2.length; i++) {
            array2[i] = array1[i];
        }
        array2[0] = 33;
        for (int i : array1) System.out.print(i + " "); // 2 3 5 7 11 13 17 19
        System.out.println();
        for (int i : array2) System.out.print(i + " "); // 33 3 5 7 11 13 17 19
    }
}

简单理解如下图

3.4 数组元素的反转

数组的反转,主要是找个临时变量 temp 进行替换操作即可

java 复制代码
public class ArrayTest5 {
    public static void main(String[] args) {
        String[] arr = new String[]{"SS", "QQ", "YY", "XX", "TT", "KK", "EE", "GG", "KK"};
        // 数组的反转,方法 1
        for (int i = 0; i < arr.length / 2; i++) {
            String temp = arr[i];
            arr[i] = arr[arr.length - i -1];
            arr[arr.length - i -1] = temp;
        }
        for (String i : arr) System.out.print(i + " "); // KK GG EE KK TT XX YY QQ SS 
    }
}

3.5 数组中指定元素的查找

线性查找

java 复制代码
/**
 * 元素的查找:线性查找
 * 
 */
public class ArrayTest6 {
    public static void main(String[] args) {
        String[] arr = new String[]{"SS", "QQ", "YY", "XX", "TT", "KK", "EE", "GG", "KK"};

        String dest = "YY";
        boolean isFlag = true;
        // 线性查找:通过遍历的方式,一个一个数据进行比较,查找。具体普遍适用性
        for (int i = 0; i < arr.length; i++) {
            if(dest.equals(arr[i])){
                System.out.println("找到了指定元素,索引位置为: " + i);
                isFlag = false;
                break;
            }
        }
        if(isFlag) System.out.print("很遗憾,没有找到元素");
    }
}

二分查找

java 复制代码
/**
 * 二分查找,前提是,所要查找的数组必须有序
 * 思路:每次比较中间值,折半的方式检索
 * 
 */
public class ArrayTest7 {
    public static void main(String[] args) {
        int[] arr2 = new int[]{-12, -8, 2, 12, 33, 35, 78, 89, 99, 112, 333, 456};
        int dest = 33;
        int head = 0; // 初始的首索引
        int end = arr2.length - 1; // 初始的末索引
        boolean isFlag = true;
        while (head <= end) {
            int minddle = (head + end) / 2;
            if (dest == arr2[minddle]) {
                System.out.println("找到了指定元素,索引位置为:" + minddle);
                isFlag = false;
                break;
            } else if (arr2[minddle] > dest) {
                end = minddle - 1;
            } else {
                head = minddle + 1;
            }
        }
        if (isFlag) System.out.print("很遗憾,没有找到元素");
    }
}

3.6 数组的排序算法

「十大排序算法」

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

一些理解内容

  1. 衡量排序算法的优劣:时间复杂度、空间复杂度、稳定性
  2. 排序的分类:内部排序 与 外部排序(需要借助磁盘)
  3. 不同排序算法的性能对比

算法的五大特性

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

3.6.1 冒泡排序

冒泡排序的基本思想:通过对待排序序列从前向后,依次比较相邻元素的排序码,若发现逆序则交换,使排序码较大的元素逐渐从前部移向后部。

因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序, 因此要在排序过程中设置一个标志 swap 判断元素是否进行过交换。从而减少不必要的比较。

java 复制代码
public class ArrayTest8 {
    public static void main(String[] args) {
        int[] arr = new int[]{2, 44, 11, 23, 1, 6, 47, 89, 56, 62, 38, 68, 46, 35, 99};
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        for(int i : arr) System.out.print(i + " ");
    }
}

3.6.2 快速排序

这里暂时先不写,后续补充,目前冒泡,快速排序是必须掌握的内容

5. Arrays 工具类的使用

定义在 java.util 包下,Arrays 工具类提供了很多操作数组的方法,更多方法可参考 API 文档

boolean equals(int[] a, int[] b) 判断两个数组是否相等
String toString(int[] a) 输出数组信息
void fill(int[] a,int val) 将指定值填充到数组之中
void sort(int[] a) 对数组进行排序
int binarySearch(int[] a,int key) 对排序后的数组进行二分法检索指定的值
java 复制代码
import java.util.Arrays;

/**
 * Arrays 工具类的使用
 * 
 */
public class ArrayTest9 {
    public static void main(String[] args) {
        int[] arr1 = new int[]{1, 2, 3, 4};
        int[] arr2 = new int[]{9, 5, 3, 7};
        // 判断两个数组是否相等
        boolean isEquals = Arrays.equals(arr1, arr2);
        System.out.println(isEquals); // false

        // 输出数组信息
        System.out.println(Arrays.toString(arr1)); // [1, 2, 3, 4]

        // 将指定的值填充到数组中
        Arrays.fill(arr1, 10);
        System.out.println(Arrays.toString(arr1)); // [10, 10, 10, 10]

        // 对数组进行排序
        Arrays.sort(arr2);
        System.out.println(Arrays.toString(arr2)); // [3, 5, 7, 9]

        // 对排序后的 arr2 进行二分法检索指定的值
        int index = Arrays.binarySearch(arr2, 5);
        if (index >= 0) {
            System.out.println(index); // 1
        } else {
            System.out.println("未找到");
        }
    }
}

6. 数组使用中的常见异常

java 复制代码
/**
 * 数组中常见异常:
 * 1. ArrayIndexOutOfBoundsException 数组角标越界异常
 * 2. NullPointerException 空指针异常
 * 
 */
public class ArrayTest10 {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
        // System.out.println(arr[9]); 触发 ArrayIndexOutOfBoundsException 异常

        // NullPointerException 空指针异常
        int[] arr1 = new int[]{1, 2, 3};
        // 情况 1
        // arr1 = null;
        // System.out.println(arr1[0]);

        // 情况 2
        // int[][] arr2 = new int[5][];
        // System.out.println(arr2[0][0]);

        // 情况 3
        // String[] arr3 = new String[]{"aa", "bb", "cc"};
        // arr3[0] = null;
        // System.out.println(arr3[0].toString());
    }
}

提示:一旦程序出现异常,未处理时,就终止执行。

相关推荐
双力臂4041 小时前
Java IO流体系详解:字节流、字符流与NIO/BIO对比及文件拷贝实践
java·开发语言·nio
钮钴禄·爱因斯晨2 小时前
Java API (二):从 Object 类到正则表达式的核心详解
java·开发语言·信息可视化·正则表达式
Monkey-旭2 小时前
Android 蓝牙通讯全解析:从基础到实战
android·java·microsoft·蓝牙通讯
BoneToBone3 小时前
java list 与set 集合的迭代器在进行元素操作时出现数据混乱问题及原因
java·开发语言·list
WanderInk3 小时前
深入解析:Java Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); 一行代码的背后功力
java·后端·算法
O执O3 小时前
JavaWeb笔记四
java·hive·hadoop·笔记·web
二十雨辰4 小时前
[尚庭公寓]11-用户与系统管理
java·服务器·spring
Dajiaonew4 小时前
从零搭建Cloud Alibaba
java·数据库·分布式·微服务
布朗克1684 小时前
jvm分析篇---1、先认识下dump文件
java·jvm·内存·dump
@陌陌5 小时前
力扣(1957,128) - day01
java·python·算法