Java 数组

数组


什么是数组?

  • 数组(Array)  多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。

  • 数组中的概念

    • 数组名

    • 下标(或索引)

    • 元素

    • 数组的长度

数组的特点

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。

  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。

  • 数组中的元素在内存中是依次紧密排列的,有序的。

  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改

  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。

  • 数组名中引用的是这块连续空间的首地址。

数组的分类

1、按照元素类型分:

  • 基本数据类型元素的数组:每个元素位置存储基本数据类型的值

  • 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)

2、按照维度分:

  • 一维数组:存储一组数据

  • 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。

 

 一维数组的使用


一维数组的声明 

格式:

//推荐
元素的数据类型[] 一维数组的名称;

//不推荐
元素的数据类型  一维数组名[];

 举例:

int arr[];
int[] arr;
double[] arr;
String[] arr; //引用类型数组

数组的声明,需要明确:

(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。

(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。

(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。

 注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法  

一维数组的初始化 

静态初始化
  • 如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。

  • 静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。

  • 一维数组声明和静态初始化格式1:

  • 数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...}; ​ 
    
    或  
       
    数据类型[] 数组名; 
    
    数组名 = new 数据类型[]{元素1,元素2,元素3,...};
    

new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组实体 

例如,定义和储存包含整数 1、 2、3、4、5的整数容器就

int[] arr = new int[]{1,2,3,4,5};
//或
int[] arr;
arr = new int[]{1,2,3,4,5};

 一维数组声明和静态初始化格式2:

数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分成两个语句写

  

动态初始化 

数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。

动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。

格式:

数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];

数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];

  • [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。

  • 注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。

正确写法:

 int[] arr = new int[5];

 int[] arr;

 arr = new int[5];

错误写法

int[] arr = new int[5]{1,2,3,4,5}; //当后边有{}指定元素列表,就不需要在[]中指定元素个数了

一维数组的使用


数组的长度
  • 数组的元素总个数,即数组的长度

  • 每个数组都有一个属性length指明它的长度,例如:arr.length 指明数组arr的长度(即元素个数)

  • 每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的

class exercise{
    public static void main(String[] args) {
        int[] scores;
        scores = new int[]{1,2,3,4,5};
        //int scores[] = new int[]{1,2,3,4,5};
        System.out.println(scores.length); //5
        int scores2[] = new int[6];
        System.out.println(scores2.length); //6
    }
}
数组元素的引用
如何表示数组中的一个元素?

每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标,可以通过数组的索引/下标访问到数组中的元素。

数组名[索引/下标]

数组的下标范围?

Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]

数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];

一维数组的遍历

将数组中的每个元素分别获取出来,就是遍历。for循环与数组的遍历是绝配。

举例

 

  

数组元素的默认值

数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:

 对于基本数据类型而言,默认初始化值各有不同。

对于引用数据类型而言,默认初始化值为null(注意与0不同!)

public class ArrayTest7 {
    public static void main(String[] args) {
        //存储26个字母
        char[] letters = new char[26];
        System.out.println("letters数组的长度:" + letters.length);
        System.out.print("存储字母到letters数组之前:[");
        for (int i = 0; i < letters.length; i++) {
            if(i==0){
                System.out.print(letters[i]);
            }else{
                System.out.print("," + letters[i]);
            }
        }
        System.out.println("]");

       //存储5个姓名
        String[] names = new String[5];
        System.out.println("names数组的长度:" + names.length);
        System.out.print("存储姓名到names数组之前:[");
        for (int i = 0; i < names.length; i++) {
            if(i==0){
                System.out.print(names[i]);
            }else{
                System.out.print("," + names[i]);
            }
        }
        System.out.println("]");
    }
}

运行结果 

一维数组内存分析


Java虚拟机的内存划分

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

 

一维数组在内存中的存储

一个一维数组内存图

 

数组下标为什么是0开始

因为第一个元素距离数组首地址间隔0个单元格。

两个一维数组内存图

两个变量指向一个一维数组

两个数组变量本质上代表同一个数组。

public static void main(String[] args) {
    // 定义数组,存储3个元素
    int[] arr = new int[3];
    //数组索引进行赋值
    arr[0] = 5;
    arr[1] = 6;
    arr[2] = 7;
    //输出3个索引上的元素值
    System.out.println(arr[0]);
    System.out.println(arr[1]);
    System.out.println(arr[2]);
    //定义数组变量arr2,将arr的地址赋值给arr2
    int[] arr2 = arr;
    arr2[1] = 9;
    System.out.println(arr[1]);
}

 

一维数组的应用


输出对应的英文

用一个数组,保存星期一到星期天的7个英语单词,从键盘输入1-7,显示对应的单词 {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}  

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

import  java.util.Scanner;
public class scratch{
    public static void main(String[] args) {
        //获取学生人数 确定数组长度
        System.out.println("请输入学生人数");
        Scanner scanner = new Scanner(System.in);
        int count = scanner.nextInt();

        // 创建指定长度的数组
        int[] scores = new int[count];

        // 使用循环,依次给数组的元素赋值
        int maxScore = 0; //记录最高分
        System.out.println("请输入" + count + "个成绩");
        for (int i = 0;i < scores.length;i++){
            scores[i] = scanner.nextInt();

            // 获取数组中元素的最大值,即为最高分
            if(maxScore < scores[i]){
                maxScore = scores[i];
            }
        }
        System.out.println("最高分是" + maxScore);

        //遍历数组元素,输出各自的分数,并根据其分数与最高分的差值,获取各自的等级
        char grade;
        for(int i = 0;i < scores.length;i++){
            if(scores[i] >= maxScore - 10){
                grade = 'A';
            }else if(scores[i] >= maxScore - 20){
                grade = 'B';
            }else if(scores[i] >= maxScore - 30){
                grade = 'C';
            }else{
                grade = 'D';
            }
            System.out.println("student " + i + " socre is " + scores[i] + ", grade is " + grade);
        }
        //关闭资源
        scanner.close();
    }
}

 

快速排序

快速排序(Quick Sort)由图灵奖获得者Tony Hoare发明,被列为20世纪十大算法之一,是迄今为止所有内排序算法中速度最快的一种,快速排序的时间复杂度为O(nlog(n))。

快速排序通常明显比同为O(nlogn)的其他算法更快,因此常被采用,而且快排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。

排序思想:

  1. 从数列中挑出一个元素,称为"基准"(pivot),

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

  4. 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

动态演示:排序(冒泡排序,选择排序,插入排序,归并排序,快速排序,计数排序,基数排序) - VisuAlgo

内部排序性能比较与选择
  • 性能比较

    • 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归并排序。

    • 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。

    • 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排序、快速排序、 Shell排序和堆排序是不稳定排序

    • 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序。

  • 选择

    • 若n较小(如n≤50),可采用直接插入或直接选择排序。 当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。

    • 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;

    • 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。

 

Arrays工具类的使用

java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:

  • 数组元素拼接static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号("[]")中。相邻元素用字符 ", "(逗号加空格)分隔。形式为:[元素1,元素2,元素3...]

    • static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号("[]")中。相邻元素用字符 ", "(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。

  • 数组排序

    • static void sort(int[] a) :将a数组按照从小到大进行排序

    • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列

    • static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。

    • static <T> void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。

  • 数组元素的二分查找

    • static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。

  • 数组的复制

    • static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组

    • static <T> T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组

    • static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组

    • static <T> T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组

  • 比较两个数组是否相等

    • static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同

    • static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同

  • 填充数组

    • static void fill(int[] a, int val) :用val值填充整个a数组

    • static void fill(Object[] a,Object val):用val对象填充整个a数组

    • static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值

    • static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象

举例:java.util.Arrays类的sort()方法提供了数组元素排序功能:

import java.util.Arrays;
public class SortTest {
    public static void main(String[] args) {
        int[] arr = {3,2,5,1,6};
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr); //升序排列
        System.out.println(Arrays.toString(arr));
    }
}

 

数组的常见异常

数组角标越界异常

当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会报数组下标越界异常:ArrayIndexOutOfBoundsException。

数组元素下标范围[0,数组名.length-1]

public class TestArrayIndexOutOfBoundsException {
    public static void main(String[] args){
        int[] arr = {1,2,3};
        System.out.println(arr[3]); //下标越界
        System.out.println(arr[arr.length]); //下标越界
        //数组下标范围 [0,length-1]
    }
}

空指针异常

​​​​​​​

public class TestNullpointerException {
    public static void main(String[] args) {
        //定义数组
        int[][] arr = new int[3][];

        System.out.println(arr[0][0]);//NullpointerException
    }
}

 因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException 空指针异常。

 

		//举例一:
//		int[] arr1 = new int[10];
//		arr1 = null;
//		System.out.println(arr1[9]);
		
		//举例二:
//		int[][] arr2 = new int[5][];
//		//arr2[3] = new int[10];
//		System.out.println(arr2[3][3]);
		
		//举例三:
		String[] arr3 = new String[10];
		System.out.println(arr3[2].toString());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值