Java零基础入门笔记:(5)数组

 前言

本笔记是学习狂神的java教程,建议配合视频,学习体验更佳。

【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili

第1-2章:Java零基础入门笔记:(1-2)入门(简介、基础知识)-优快云博客

第3章:Java零基础入门笔记:(3)程序控制-优快云博客

第4章:Java零基础入门笔记:(4)方法-优快云博客

-

-

数组

数组的定义

数组是相同类型数据的有序集合.

在 Java 中,数组是一种用于存储固定大小的同类型数据的容器。它允许你将多个具有相同数据类型的元素组织在一起,并通过索引来访问和操作这些元素。数组的大小在创建时确定,并且在运行时不能改变。

数组可以存储基本数据类型(如 intdoublechar 等)或对象类型(如 String、自定义类等)。数组的每个元素都有一个唯一的索引,从 0 开始,直到 length - 1,其中 length 是数组的长度。

-

数组声明与创建

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

dataType[] arrayRefVar; //首选的方法
或
dataType arrayRefVar[]; //效果相同,但不是首选方法

Java语言使用new操作符来创建数组,语法如下:

arrayRefVar = new dataType[arraySize];

当然,以上方法可以总结成一句话: 

dataType[] arrayRefVar = new dataType[arraySize];

下面是给数组变量赋值的语法:

arrayRefVar[index] = value

下面是从数组中取值的语法:

arrayRefVar[index]

 下面的代码演示了以上过程,首先声明并初始化一个整数数组,然后通过循环为数组的每个元素赋值,并最终打印数组中索引为 5 的元素值。

package array;

public class demo1 {
    public static void main(String[] args) {
        int[] nums;     // 1.声明数组
        nums = new int[10];     //  2.创建一个数组

        // 赋值
        for (int i=0;i<10;i++){
            nums[i] = i;
        }

        // 取值
        System.out.println(nums[5]);

    }
}

获取数组长度:

arrays.length

-

内存分析

在 Java 中,堆、栈和方法区是 Java 虚拟机(JVM)内存模型中的三个关键区域,它们共同管理程序运行时的内存分配和使用。

堆是 Java 中最大的内存区域,用于存储对象实例和数组。它是所有线程共享的内存空间,对象的生命周期由垃圾回收器管理。当程序运行时,通过 `new` 关键字创建的对象会被分配在堆内存中,垃圾回收器会定期清理不再使用的对象,以释放内存空间。

栈是线程私有的内存区域,每个线程在创建时都会分配一个栈空间。栈用于存储局部变量、方法调用的上下文信息以及部分结果。当方法被调用时,JVM 会为该方法创建一个栈帧,并将其压入当前线程的栈中。栈帧中包含了方法的局部变量表、操作数栈和方法的返回地址等信息。方法执行完毕后,栈帧会被弹出,局部变量和方法上下文信息随之销毁。由于栈的生命周期与线程绑定,栈中的数据访问速度快,但栈的大小通常有限制,过深的递归调用可能导致栈溢出。

方法区同样是所有线程共享的内存区域,用于存储类的结构信息,如常量池、字段和方法数据、构造函数和普通方法的字节码内容等。方法区是 JVM 规范中定义的一个逻辑区域,它在 HotSpot JVM 实现中被称为永久代(Permanent Generation)。永久代在 Java 8 及以后版本中被元空间(Metaspace)所取代,元空间使用本地内存来存储类的元数据,从而避免了永久代的内存限制问题。方法区的内存管理相对复杂,因为它需要处理类的加载、卸载以及常量的存储等操作。

总的来说,堆主要用于对象的存储和管理,栈用于方法调用和局部变量的存储,而方法区则用于存储类的结构信息和常量。

声明时在栈,new时在堆

数组初始化

  • 静态初始化:创建+赋值
  • 动态初始化:创建,数组里面包含默认值
  • 数组的默认初始化
    • 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。如int里面默认初始化为0

静态初始化

数组的静态初始化是指在声明数组时直接为数组元素赋值,而不需要显式调用 `new` 来分配内存。这种方式在编译时完成初始化,因此称为静态初始化。

在静态初始化中,数组的大小由初始化列表中的元素数量决定,不需要显式指定数组长度。初始化列表用大括号 `{}` 包裹,元素之间用逗号分隔。例如,声明并初始化一个整数数组时,可以直接写为 `int[] arr = {1, 2, 3, 4};`。

下面的代码演示了如何进行静态初始化

package array;

public class demo1 {
    public static void main(String[] args) {
        //静态初始化:创建 + 赋值
        int[] a = {1,2,3,4,5,6,7,8};
        System.out.println(a[0]);
    }
}
1

 动态初始化

数组的动态初始化是指在运行时通过 `new` 关键字为数组分配内存,并指定数组的长度,而数组的具体元素值可以在后续操作中赋值。这种方式允许在程序运行过程中根据需要动态确定数组的大小,提供了更大的灵活性。

动态初始化时,数组的初始值会根据其数据类型自动赋予默认值,例如,整型数组的默认值为 `0`,布尔型数组的默认值为 `false`。通过动态初始化,可以在运行时根据实际需求创建合适大小的数组,从而避免了静态初始化中数组大小固定的限制。

下面的代码演示了如何进行动态初始化

package array;

public class demo1 {
    public static void main(String[] args) {

        //动态初始化:包含默认初始化
        int[] b = new int[10];
        b[0] = 10;
        b[1] = 10;

        System.out.println(b[0]);
        System.out.println(b[1]);
    }
}
10
10

数组的默认初始化 

数组的初始值会根据其数据类型自动赋予默认值,例如,整型数组的默认值为 `0`,布尔型数组的默认值为 `false`。

package array;

public class demo1 {
    public static void main(String[] args) {

        //动态初始化:包含默认初始化
        int[] b = new int[10];
        b[0] = 10;
        b[1] = 10;

        System.out.println(b[0]);
        System.out.println(b[1]);
        System.out.println(b[2]);    // 默认初始化
        System.out.println(b[3]);    // 默认初始化
    }
}
10
10
0
0

-

数组越界

数组的四个基本特点

  • 长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
  • 元素必须是相同类型,不允许出现混合类型。
  • 数组中的元素可以是任何数据类型,包括基本类型和引用类型
  • 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的

-

数组边界:下标的合法区间:[0, length-1],如果超过这个边界就会报错;

  • ArrayIndexOutOfBoundsException:数组下标越界异常!

下面的代码演示了数组越界代码

int[] a=new int[2];
System.out.println(a[2]);

-

数组使用

下面的代码演示了如何结合for循环遍历数组

package array;

public class demo1 {
    public static void main(String[] args) {
        int[] arrays = {1, 2, 3, 4, 5};

        // 打印全部数组元素
        for (int i = 0; i < arrays.length; i++) {
            System.out.print(arrays[i]);
        }
        System.out.println();

        // 计算所有数组元素的和
        int sum = 0;
        for (int i = 0; i < arrays.length; i++) {
            sum += arrays[i];
        }
        System.out.println("sum="+sum);

        // 查找数组中的最大值
        int max = arrays[0];
        for (int i = 1; i < arrays.length; i++) {
            if (arrays[i] > max)
                max = arrays[i];
        }
        System.out.println("max="+max);
    }
}

输出结果如下: 

12345
sum=15
max=5

下面的代码演示了For-Each循环(for循环增强):增强型 for 循环会依次取出数组中的每个元素,并将其赋值给循环变量 array,然后执行循环体中的代码。

package array;

public class demo1 {
    public static void main(String[] args) {
        int[] arrays = {1, 2, 3, 4, 5};

        // 打印全部数组元素
        for (int array : arrays) {
            System.out.print(array);
        }
        System.out.println();
    }
}
12345

下面的代码演示了数组作方法的参数:

  • main 方法中,调用了 printArray 方法,并将数组 arrays 作为参数传递给该方法。

  • 这个 printArray 方法接收一个整数数组作为参数
package array;

public class demo1 {
    public static void main(String[] args) {
        int[] arrays = {1, 2, 3, 4, 5};

        // 打印全部数组元素
        printArray(arrays);
        System.out.println();
    }

    public static void printArray(int[] arrays){
        for (int array: arrays) {
            System.out.print(array);
        }
    }
}

    下面的代码演示了 数组作返回值:

    • 调用 reverseArray 方法,传入原始数组 arrays,并返回一个新的数组 re_array,其中元素顺序与原始数组相反。
    • reverseArray 方法
      • 创建一个新的数组 result,其长度与原始数组相同。

      • 使用循环将原始数组的元素按逆序赋值给新数组。

      • 返回翻转后的数组。

    package array;
    
    public class demo1 {
        public static void main(String[] args) {
            int[] arrays = {1, 2, 3, 4, 5};
    
            // 打印全部数组元素
            printArray(arrays);
            System.out.println();
    
            int[] re_array = reverseArray(arrays);
            printArray(re_array);
        }
    
        public static void printArray(int[] arrays){        // 打印数组
            for (int array: arrays) {
                System.out.print(array);
            }
        }
    
        public static int[] reverseArray(int[] arrays) {    // 翻转数组
            int[] result = new int[arrays.length];
    
            for (int i = 0; i < arrays.length; i++) {
                result[i] = arrays[arrays.length-i-1];
            }
            return result;
        }
    }
    
    12345
    54321

     -

    多维数组

    多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。

    二维数组定义

    如下二维数组a 可以看成一个两行五列的数组。

    int a[][] = new int[2][5];
    

    多维数组的使用 

    num[1][0];
    

    下面的代码演示了如何使用二维数组

    package array;
    
    public class demo1 {
        public static void main(String[] args) {
            int[][] arrays = {{1, 2}, {3, 4}, {5, 6}};
    
            System.out.println(arrays[0]);      // [I@10f87f48
            System.out.println(arrays[0][0]);   // 1
    
            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();
            }
    
        }
    }
    
    [I@10f87f48
    1
    12
    34
    56

    -

    Arrays类

    数组的工具类java.util.Arrays

    由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。可查看JDK帮助文档

    Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用对象来调用(注意:是"不用”而不是"不能")

    -

    具有以下常用功能:

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

     

       打印数组toString()

      package array;
      
      import java.util.Arrays;
      
      public class demo1 {
          public static void main(String[] args) {
              int[][] array = {{1,2},{2,3},{3,4},{4,5}};
              int[] a = {1,2,3,53,32,556,433,4};
      
              System.out.println(Arrays.toString(array));     // 打印数组
              System.out.println(Arrays.toString(a));     // 打印数组
          }
      }
      
      [[I@10f87f48, [I@b4c966a, [I@2f4d3709, [I@4e50df2e]
      [1, 2, 3, 53, 32, 556, 433, 4]

      打印二维数组可使用:

      // 打印二维数组
      System.out.println(Arrays.deepToString(array));  // 输出:[[1, 2], [2, 3], [3, 4], [4, 5]]

       toString的实现原理:写一个自己的方法复现

      • 使用 for 循环遍历数组:

        • 如果当前元素不是数组的最后一个元素,则打印元素值后加逗号和空格(a[i] + ", ")。

        • 如果是最后一个元素,则只打印元素值。

      • 最后,打印右方括号 ] 并换行。

      package array;
      
      import java.util.Arrays;
      
      public class demo1 {
          public static void main(String[] args) {
              int[][] array = {{1,2},{2,3},{3,4},{4,5}};
      
              int[] a = {1,2,3,53,32,556,433,4};
              printArray(a);
      
          }
          public static void printArray(int[] a){
              System.out.print("[");
              for (int i = 0; i < a.length; i++) {
                  if (i != a.length-1){
                      System.out.print(a[i]+", ");
                  }else{
                      System.out.print(a[i]);
                  }
              }
              System.out.println("]");
          }
      
      }
      

      排序sort()

      Arrays.sort 方法用于对数组进行排序,默认按照升序排列数组元素。它使用了高效的排序算法(如双轴快速排序或归并排序),能够快速完成排序任务。

      package array;
      
      import java.util.Arrays;
      
      public class demo1 {
          public static void main(String[] args) {
              int[] a = {1,2,3,53,32,556,433,4};
      
              System.out.println(Arrays.toString(a));     // 打印数组
              Arrays.sort(a);         // 排序
              System.out.println(Arrays.toString(a));
          }
      }
      
      [1, 2, 3, 53, 32, 556, 433, 4]
      [1, 2, 3, 4, 32, 53, 433, 556]

      填充 fill

      Arrays.fill 是 Java 标准库中的一个实用方法,用于将数组中的所有元素填充为指定的值。

      package array;
      
      import java.util.Arrays;
      
      public class demo1 {
          public static void main(String[] args) {
              int[] a = {1,2,3,53,32,556,433,4};
      
              System.out.println(Arrays.toString(a));     // 打印数组
              Arrays.fill(a, 0);         // 排序
              System.out.println(Arrays.toString(a));
          }
      }
      
      [1, 2, 3, 53, 32, 556, 433, 4]
      [0, 0, 0, 0, 0, 0, 0, 0]
      

      -

      -

      冒泡排序

      冒泡排序是一种简单的排序算法,它通过重复遍历数组,比较相邻的两个元素,并在必要时交换它们的位置,从而逐步将数组中的元素按顺序排列。排序过程中,较小的元素会像气泡一样逐渐“浮”到数组的前面,而较大的元素则会“沉”到数组的后面,这也是“冒泡排序”名称的由来。每次遍历至少保证一个元素移动到它最终的位置,因此冒泡排序的时间复杂度为O(n²),其中n是数组的长度。

      package array;
      
      import java.util.Arrays;
      
      //冒泡排序
      //1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置。
      //2.每一次比较,都会产生出一个最大,或者最小的数字。
      //3.下一轮则可以少一次排序!
      //4.依次循环,直到结束!
      
      public class demo1 {
          public static void main(String[] args) {
              int[] a = {1,4,5,6,72,2,2,2,25,6,7};
              System.out.println(Arrays.toString(a));
              int[] b = sort(a);
              System.out.println(Arrays.toString(b));
          }
      
          public static int[] sort(int[] array){
              int temp = 0;       // 临时变量
              for (int i = 0; i < array.length; i++) {    // 外层循环,判断我们这个要走多少次
                  for (int j = 0; j < array.length-i-1; j++) {      // 内层循环,比较判断两个数,如果第一个数,比第二个数大,则交换位置
                      if (array[j+1]>array[j]){
                          temp = array[j];
                          array[j] = array[j+1];
                          array[j+1] = temp;
                      }
                  }
              }
              return array;
          }
      }
      

      这个算法的时间复杂度为O(n²)。

      思考:如何优化? 答:通过flag标识位减少没有意义的比较

      package array;
      
      import java.util.Arrays;
      
      //冒泡排序
      //1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置。
      //2.每一次比较,都会产生出一个最大,或者最小的数字。
      //3.下一轮则可以少一次排序!
      //4.依次循环,直到结束!
      
      public class demo1 {
          public static void main(String[] args) {
              int[] a = {1,4,5,6,72,2,2,2,25,6,7};
              System.out.println(Arrays.toString(a));
              int[] b = sort(a);
              System.out.println(Arrays.toString(b));
          }
      
          public static int[] sort(int[] array){
              int temp = 0;       // 临时变量
              for (int i = 0; i < array.length; i++) {    // 外层循环,判断我们这个要走多少次
                  boolean flag = false; // 通过flag标识位减少没有意义的比较
                  for (int j = 0; j < array.length-i-1; j++) {      // 内层循环,比较判断两个数,如果第一个数,比第二个数大,则交换位置
                      if (array[j+1]>array[j]){
                          temp = array[j];
                          array[j] = array[j+1];
                          array[j+1] = temp;
                          flag = true;
                      }
                  }
                  if (flag==false){
                      break;
                  }
              }
              return array;
          }
      }
      

      -

      -

      稀疏数组

      分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据。

      解决方法:稀疏数组

      稀疏数组介绍

      当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。

      稀疏数组的处理方式是:

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

      如下图:左边是原始数组,右边是稀疏数组

       需求:使用稀疏数组保存五子棋游戏的棋局,有存盘退出和续上盘的功能

      package array;
      import java.util.Arrays;
      
      
      public class demo1 {
          public static void main(String[] args) {
              // 1. 创建一个二维数组11*11   0:没有棋子    1:黑棋    2:白棋
              int[][] array1 = new int[11][11];
              array1[1][2] = 1;
              array1[2][3] = 2;
      
              for (int[] ints : array1) {
                  for (int anInt : ints) {
                      System.out.print(anInt+"\t");
                  }
                  System.out.println();
              }
      
              //获取有效值的个数
              int sum = 0;
              for (int i = 0; i < 11; i++) {
                  for (int j = 0; j < 11; j++) {
                      if (array1[i][j]!=0){
                          sum++;
                      }
                  }
              }
              System.out.println("有效值的个数:"+sum);
      
      
              // 2.创建一个稀疏数组的数组
              int[][] array2 = new int[sum+1][3];
              array2[0][0] = 11;      // 多少行
              array2[0][1] = 11;      // 多少列
              array2[0][2] = sum;     // 总共几个有效数
      
              int count = 0;
              for (int i = 0; i < array1.length; i++) {
                  for (int j = 0; j < array1[i].length; j++) {
                      if (array1[i][j]!=0){
                          count++;
                          array2[count][0] = i;
                          array2[count][1] = j;
                          array2[count][2] = array1[i][j];
                      }
                  }
              }
      
              //输出稀疏数组
              System.out.println("稀疏数组");
      
              for (int i = 0; i < array2.length; i++) {
                  System.out.println(array2[i][0]+"\t"
                          +array2[i][1]+"\t"
                          +array2[i][2]+"\t");
              }
      
      
              // 3.还原
              System.out.println("====================");
              System.out.println("还原");
      
              int[][] array3 = new int[array2[0][0]][array2[0][1]];       // 1.读取稀疏数组
      
              for (int i = 1; i < array2.length; i++) {       // 2.给其中的元素还原它的值
                  array3[array2[i][0]][array2[i][1]] = array2[i][2];
              }
      
              System.out.println("输出还原的数组");      // 3.打印
              for (int[] ints : array3) {
                  for (int anInt : ints) {
                      System.out.print(anInt+"\t");
                  }
                  System.out.println();
              }
          }
      }
      

      输出原始的数组
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	1	0	0	0	0	0	0	0	0	
      0	0	0	2	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      ====================
      有效值的个数:2
      稀疏数组
      11	11	2	
      1	2	1	
      2	3	2	
      ====================
      还原
      输出还原的数组
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	1	0	0	0	0	0	0	0	0	
      0	0	0	2	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0	
      0	0	0	0	0	0	0	0	0	0	0
      

      -

      🚀 知识的力量在于分享! 🚀

      如果你从我的分享中学到了新知识,或者觉得这些内容对你有帮助,请不吝点赞、关注和收藏!这样不仅能让我更有动力,还能帮助更多人看到这些内容。

      👍 点赞:让我知道你收获了!

      👀 关注:获取更多干货!

      ⭐️ 收藏:方便随时复习!

      感谢您的支持,我们一起进步!

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值