Java中的数组

文章目录

  • 一、数组的核心特性
  • 二、数组的定义与初始化
    • [2.1 声明数组](#2.1 声明数组)
    • [2.2 初始化数组](#2.2 初始化数组)
  • 三、数组的基本操作
    • [3.1 访问元素](#3.1 访问元素)
    • [3.2 获取数组长度](#3.2 获取数组长度)
    • [3.3 遍历数组](#3.3 遍历数组)
    • [3.4 数组的复制](#3.4 数组的复制)
    • [3.5 数组的排序](#3.5 数组的排序)
    • [3.6 数组的查找](#3.6 数组的查找)
  • 四、数组的内存分析
  • 五、二维数组
    • [5.1 二维数组的定义与初始化](#5.1 二维数组的定义与初始化)
    • [5.2 二维数组的访问与遍历](#5.2 二维数组的访问与遍历)
    • [5.3 数组实用工具类](#5.3 数组实用工具类)

一、数组的核心特性

数组是java中存储固定大小、相同类型元素的容器,是最基础的数据结构之一,核心特性是长度不可变,索引从0开始。下面小编从定义、使用、内存、操作、多维数组等维度进行讲解。

  1. 类型统一:只能存储同一种数据类型(基本类型/引用类型),比如int数组只能存int,String数组只能存String。
  2. 长度固定:数组一旦创建,长度无法进行修改。如果想要对数组进行扩容只能新建数组并把元素复制到新数组中。
  3. 索引访问:通过数组名[索引]快速访问元素,索引范围为0 ~ 数组长度-1,越界会抛ArrayIndexOutOfBoundException。
  4. 内存连续:数组元素在内存中连续存储,这是其访问效率高的核心原因。
  5. 引用类型:数组是引用类型,数组中的数据存储在堆内存,变量存储的是数组的内存地址(引用)。

二、数组的定义与初始化

2.1 声明数组

声明数组有两种方式

java 复制代码
//方式1:推荐,更符合Java规范
数组类型[] 数组名;
//方式2:c语言风格,java也支持但不推荐
数据类型 数组名[];

//示例
int[] arr1; //声明int类型数组
String[] arr2; //声明String类型数组
double[] arr3;  //声明double类型数组

注意:声明不分配内存,此时数组名是null,不能直接进行使用。

2.2 初始化数组

初始化数组有三种方式

  1. 动态化初始(指定长度,元素默认赋值):创建数组的时候指定长度,元素会被赋予默认值(基本类型:0/0.0/false;引用类型:null)
java 复制代码
//语法:数据类型[] 数组名 = new 数组类型[长度];
public static void main(String[] args) {
        int[] arr = new int[5]; //长度为5.元素默认[0,0,0,0,0]
        String[] strArr = new String[3]; //元素默认[null,null,null]
        boolean[] boolArr = new boolean[2]; //元素默认[false,false]
}
  1. 静态初始化(指定元素,长度有元素个数进行决定):创建数组的时候直接进行赋值,长度自动等于元素个数,无需手动指定。
java 复制代码
public static void main(String[] args) {
        //标准写法:数组类型[] 数组名 = new 树蕨类型[]{元素1,元素2,...};
        int[] arr1 = new int[]{1,2,3,4,5};//长度5,元素[1,2,3,4,5]

        //错误写法,如果使用静态初始化,不可以在赋值的时候同时指定数组的大小,数组的大小由赋值的个数决定
        //int[] arr2 = new int[6]{1,2,3,4,5};
        
        //简化写法:数组类型[] 数组名 = {元素1,元素2,....};
        int[] arr3 = {1,2,3,4,5};//长度5,元素[1,2,3,4,5]
    }

注意:简化写法{}只能在声明时使用,以下写法错误:

java 复制代码
int[] arr;
arr = {1,2,3};//编译报错!!!
arr = new int[]{1,2,3};//正确,必须用new
  1. 匿名数组(无变量名,临时使用):直接创建数组并使用,无需赋值给变量,常用于方法传参,
java 复制代码
//调用的时候传入匿名数组
public static void printArray(int[] arr) {
      for (int num : arr) {
          System.out.print(num + " ");
      }
}
public static void main(String[] args) {
      printArray(new int[]{1,2,3,4,5});
}

三、数组的基本操作

3.1 访问元素

数组中访问元素通过索引进行访问

java 复制代码
public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        //访问第一个元素,索引为0
        int first = arr[0]; //输出1
        //访问最后一个元素,索引为长度-1
        int last = arr[arr.length - 1]; //输出5
        //修改元素
        arr[2] = 999; //数组变为[1,2,999,4,5]
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }

注意:索引不可越界,如arr[ - 1]或者 arr[ 5] 会导致报错,抛出ArrayIndexOutOfBoundException。

3.2 获取数组长度

数组有length属性(注意:不是方法,无括号),返回数组的元素个数。

java 复制代码
public static void main(String[] args) {
      int[] arr = {1,2,3,4};
      System.out.println(arr.length); //4
}

区分:string中的length()是方法存在括号,数组中的length是属性(无括号)。

3.3 遍历数组

遍历数组有三种方式

  • ( 1 ) 普通for循环(最常用,支持索引操作)
java 复制代码
public static void main(String[] args) {
    int[] arr = {1,2,3,4};
    for (int i = 0; i < arr.length; i++) {
        System.out.print(arr[i] + " ");
    }
}
  • ( 2 ) 增强for循环(foreach,仅遍历元素,无索引)
java 复制代码
public static void main(String[] args) {
    int[] arr = {1,2,3,4};
    for (int num : arr) {
        System.out.print(num + " ");
    }
}

注意:增强for循环无法修改数组元素,仅读取,想修改需使用普通for循环

  • ( 3 ) Arrays工具类遍历(toString方法)

java.util.Arrays是数组操作工具类,toString()可直接打印数组内容(避免手动遍历)

java 复制代码
public static void main(String[] args) {
    int[] arr = {1,2,3,4};
    System.out.println(Arrays.toString(arr));
}

输出:

3.4 数组的复制

在前面说过,数组的一个特性是数组长度不可变,所以数组的复制是实现"扩容/缩容"的核心手段

  • ( 1 ) System.arraycopy(底层方法,效率最高)

语法:System.arraycopy(源数组,源起始索引,目标数组,目标起始索引,复制的长度)

java 复制代码
public static void main(String[] args) {
     int[] arr = {1,2,3,4};
     int[] arr2 = new int[arr.length];
     System.arraycopy(arr,0,arr2,0,arr.length); //从arr索引为0的位置开始复制同arr数组同样长度个元素到arr2
     System.out.println(Arrays.toString(arr2));
    //此时验证以下是否是复制的,而不是引用指向原来的数组arr
    System.out.println("----------------------");
    arr[1] = 999;
    System.out.println(Arrays.toString(arr2));
}
  • ( 2 ) Array.copyOf(常用,自动创建新的数组)
java 复制代码
public static void main(String[] args) {
        int[] arr = {1,2,3,4};

        int[] newArr = Arrays.copyOf(arr,5); //扩容到5,新元素默认0 -> [1,2,3,4,0]
        System.out.println(Arrays.toString(newArr));

        int[] newArr2 = Arrays.copyOf(arr,2); //缩容到2 -> [1,2]
        System.out.println(Arrays.toString(newArr2));
    }

输出:

  • ( 3 )Array.copyOfRange(复制指定范围)

语法:Array.copyOfRange(源数组,起始索引,结束索引),遵循左闭右开,即 [ 起始索引,结束索引)

java 复制代码
public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    int[] newArr = Arrays.copyOfRange(arr,0,3); //复制索引0~2 -> [1,2,3]
    System.out.println(Arrays.toString(newArr));
}
  • ( 4 ) for循环赋值进行复制
java 复制代码
public static void main(String[] args) {
    int[] arr = {1,2,3,4};
    int[] newArr = new int[arr.length];
    for (int i = 0; i < arr.length; i++) {
        newArr[i] = arr[i];
     }
    System.out.println(Arrays.toString(newArr));
}

3.5 数组的排序

Arrays.sort()可对数组进行升序排序(基本类型:快速排序;引用类型:归并排序)

java 复制代码
 public static void main(String[] args) {
        //基本类型排序
        int[] arr1 = {5,3,1,4,2};
        Arrays.sort(arr1);
        System.out.println(Arrays.toString(arr1)); //[1, 2, 3, 4, 5]

        //引用类型排序(String 按字典序)
        String[] arr2 = {"banana","apple","orange"};
        Arrays.sort(arr2);
        System.out.println(Arrays.toString(arr2)); //[apple, banana, orange]
        
    }

字典序(Lexicographical Order),简单说就是按照我们查字典的规则来排列字符 / 字符串 / 序列的顺序,核心是从左到右逐个比较元素,先出现不同元素的位置,谁的元素 "更小",整个序列就排在前面;若前面元素都相同,短的序列排在前面。

字符串字典序的比较步骤:

  1. 从左到右逐个字符对比,找到第一个不相同的字符;
  2. 该位置字符ASCII 码更小的字符串,整体排在前面;
  3. 若一个字符串是另一个的前缀(前面字符全相同),长度更短的排在前面。

示例:

apple < apply:前 4 个字符appl都相同,第 5 位e(101) < y(121)

abc < abcd:abc是abcd的前缀,更短所以排前面

123 < A123:第一位1(49) < A(65)

Abc < abc:第一位A(65) < a(97)

zoo < zoom:前 3 个字符zoo相同,zoo更短排前面

注意:自定义排序需实现Comparable接口或传入Comparator比较器

  • ( 2 )冒泡排序
java 复制代码
public static void swap(int[] arr,int x,int y) {
        int tmp = arr[x];
        arr[x] = arr[y];
        arr[y] = tmp;
    }
    public static void MybubbleSort(int[] arr) {
        boolean flg = true;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j+1]) {
                    swap(arr,j,j+1);
                    flg = false; //如果发生交换就说明后面的不有序
                }
            }
        }
        //当上面如果不存在交换的时候说明数组已经有序了,后面则不需要进行交换,直接返回
        if(flg) {
            return;
        }
    }
    public static void main(String[] args) {
        int[] arr = {7,5,9,10,3,4,2,1,6,8};
        MybubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }

3.6 数组的查找

  • ( 1 )线性查找:遍历所有元素,适合无序数组
java 复制代码
 public static int find(int[] arr,int targe) {
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == targe) {
                return i; //找到返回索引
            }
        }
        return -1; //未找到返回-1
    }

    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        int targe = 3;
        int ret = find(arr,targe);
        if(ret == -1) {
            System.out.println("找不到");
        }else {
            System.out.println("找到了,下标为:" + ret);
        }
    }
  • ( 2 )二分查找:效率高(O(logn)),必须先排序,否则结果无法预测
java 复制代码
//自己自定义的方法
    public static int MyBinarySearch(int[] arr,int targe) {
        int left = 0;
        int right = arr.length - 1;
        int mid = (left + right) / 2;
        while (left <= right) {
            if(arr[mid] > targe) {
                right = mid - 1;
            }else if(arr[mid] < targe) {
                left = mid + 1;
            }else {
                return mid;
            }
            mid = (left + right) / 2;
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        int targe = 1;
        int ret = MyBinarySearch(arr,targe);
        int ret2 = Arrays.binarySearch(arr,10);
        System.out.println(ret2); //java中Arrays类的方法
        if(ret == -1) {
            System.out.println("找不到");
        }else {
            System.out.println("找到了,下标为:" + ret);
        }
    }

四、数组的内存分析

数组是引用类型,内存分配为栈内存和堆内存:

  1. 栈内存:存储数组变量(引用),比如int[] arr中的arr,存储的是数组在堆中的内存地址。
  2. 堆内存:存储数组的实际元素,数组对象本身在堆中分配连续空间。

示例:数组创建的内存过程

java 复制代码
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
  1. 栈中创建变量arr,初始为null;
  2. 堆中分配3个连续int空间(默认值为0),返回内存地址给arr;
  3. 通过arr[0]、arr[1]修改堆中元素,最终堆中元素为[10,20,0]。

数组作为方法参数时,传递的是内存地址(引用),方法内修改数组元素,会影响原来数组

java 复制代码
public static void changeArray(int[] arr) {
        arr[0] = 100; //修改堆中元素
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        changeArray(arr);
        System.out.println(arr[0]); //原数组被修改
    }

!注意:如果方法内重新给数组变量赋值(arr = new int[ 5 ]),只是改变了局部变量的引用,不会影响原来数组

java 复制代码
public static void changeArray(int[] arr) {
        arr = new int[5]; //新创建一个数组对象,让arr引用指向新的数组对象
        arr[0] = 100; //并不会影响原来数组
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        changeArray(arr);
        System.out.println(arr[0]); //原数组未被修改
    }

五、二维数组

二维数组的本质就是存储一维数组地址的数组。

5.1 二维数组的定义与初始化

  • ( 1 )动态初始化
java 复制代码
 public static void main(String[] args) {
        //语法1:指定行数和列数(规则二维数组)
        int[][] arr1 = new int[3][2]; //3行2列,元素默认为0
        //语法2:仅指定行数,列数动态分配
        int[][] arr2 = new int[2][];  //2行,列数未分配
        arr2[0] = new int[2]; //第一行2列
        arr2[1] = new int[3]; //第二行3列
    }
  • ( 2 )静态初始化
java 复制代码
public static void main(String[] args) {
        //标准写法
        int[][] arr1 = new int[][]{{1,2},{3,4,5}};
        //简化写法
        int[][] arr2 = {{1,2},{3,4,5}};
    }

5.2 二维数组的访问与遍历

  • ( 1 )访问元素
java 复制代码
 public static void main(String[] args) {
        int[][] arr = {{1,2},{3,4,5}};
        int num = arr[0][1]; //访问第一行第二列的元素 -> 2
        arr[1][2] = 999; //修改第二行第三列元素 -> 5变为999
    }
  • ( 2 )遍历元素
java 复制代码
public static void main(String[] args) {
        int[][] arr = {{1,2},{3,4,5}};
        //普通for循环
        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();
        }
        //增强for循环
        for(int[] row : arr) {
            for (int line : row) {
                System.out.print(line + " ");
            }
            System.out.println();
        }
    }
  • ( 1 )打印元素
    Arrays.toString() 仅打印一维数组,二维数组需用Arrays.deepToString()
java 复制代码
public static void main(String[] args) {
        int[][] arr = {{1,2},{3,4,5}};
        System.out.println(Arrays.deepToString(arr));
    }

5.3 数组实用工具类

Java提供Arrays工具类,封装了数组的常用操作,无需手动实现

java 复制代码
 public static void main(String[] args) {
        int[] arr = {5,3,1,4,2};
        //1.排序
        Arrays.sort(arr);
        //2.二分查找
        int index = Arrays.binarySearch(arr,3);
        //3.复制
        int[] newArr = Arrays.copyOf(arr,10);
        //4.填充:所有元素赋值为指定值
        Arrays.fill(arr,0); //arr变为【0,0,0,0,0】
        //5.比较两个数组是否相等
        int[] arr1 = {1,2,3};
        int[] arr2 = {1,2,3};
        boolean isEqual = Arrays.equals(arr1,arr2);//true
        //6.打印数组
        System.out.println(Arrays.toString(arr));
    }
相关推荐
java1234_小锋3 小时前
Java高频面试题:BIO、NIO、AIO有什么区别?
java·面试·nio
用户8307196840823 小时前
Java IO三大模型(BIO/NIO/AIO)超详细总结
java
sheji34163 小时前
【开题答辩全过程】以 基于SSM的花店销售管理系统为例,包含答辩的问题和答案
java
Mr_sun.4 小时前
Day09——入退管理-入住-2
android·java·开发语言
MAGICIAN...4 小时前
【java-软件设计原则】
java·开发语言
JH30734 小时前
为什么switch不支持long
java
盐真卿4 小时前
python第八部分:高级特性(二)
java·开发语言
上海合宙LuatOS4 小时前
LuatOS核心库API——【audio 】
java·网络·单片机·嵌入式硬件·物联网·音视频·硬件工程
汤姆yu4 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端