Big_DATA_004 Java 数组

4、数组

重点:
1、数组的内存分析(引用数据类型)
2、数组的排序

难点:
1、数组的内存分析(引用数据类型)
1、会定义数组、实例化数组、元素访问
2、数组是一个引用数据类型
3、数组的排序

4.1、数组的简介

1、数组,其实是一个容器。是一个用来存储相兼容数据类型的数据的一个容器。

2、数组的使用场景:当需要使用到多个相同数据类型的变量的时候。

3、数组的特点:数组的长度是不可变的,一个数组实例化完成以后,长度将不能发生改变。

长度:其实就是数组的容量,代表这个数组中能够存储多少个数据。 .length

元素:数组中存储的每一个数据,称为这个数组中的元素。

4.2、数组的定义与实例化

4.2.1、数组的定义

在定义数组的时候,用 [] 来表示一个数组。

int[] array;		// 声明一个数组,这个数组中存储的数据都是int类型的数据。
boolean[] array;	// 声明一个数组,这个数组中存储的数据都是boolean类型的数据。
int array[];		// 这是C语言中定义数组的方法,这种方式,Java也是支持的。
如何看一个变量的类型?
盖住变量名,剩下的部分,就是这个变量的类型。
4.2.2、数组的实例化

实例化:开辟空间,并在这个空间中填充一些初始值。

new:

1、数组的实例化,需要用new完成。

2、以后,再在任意位置,只要是看到了new,都表示在堆内存上开辟空间。

// 默认值:
// 整型的默认值是 0
// 浮点型的默认值是 0.0
// 布尔型的默认值是 false
// 字符型的默认值是 '\0' '\u0000'

// 实例化一个数组,定义数组的长度为5。此时这个数组中的元素,填充的是指定数据类型的默认值
int[] array = new int[5];
// 实例化一个数组,并填充数组中的元素为 1, 2, 3, 4, 5 。但是这种方式的实例化没有显示的数组长度。
// 此时,这个数组的长度,由初始值的数量决定。
int[] array = new int[] { 1, 2, 3, 4, 5 };
// 在上方的基础上,省略了 new int[]
int[] array = { 1, 2, 3, 4, 5 };
4.3、数组的内存分析

4.3.1、数组的实例化

数组的实例化,需要使用关键字new。这个操作,其实是在堆上开辟的空间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOEwvXcb-1570542300538)(./images/array_memory.png)]

1、数组的实例化,是在堆上开辟的连续的指定长度的空间。
2、然后将首元素的地址给栈中的变量进行赋值。
4.3.2、引用数据类型

引用数据类型,是Java中的两大类型之一。

如果一个变量中存储的内容,不是一个字面的值,而是一个指向了其他位置的地址。这个变量就是一个引用数据类型的变量。同时这个变量,称为指向的空间的引用。

数组,是一个引用数据类型。

与引用类型对应的,另外一种类型,叫做值类型。
变量中存储的内容,就是字面的值。
int a = 10;
而值类型,在Java中,就是基本数据类型。
4.4、数组的元素访问

4.4.1、什么是元素访问

就是使用数组中的某一块空间,往这块空间中存储数据,也可以从这块空间中读取数据。

4.4.2、如何进行元素访问

访问数组中的元素,通过下标访问。

每一个元素,在数组中存储的时候,都是有一个索引的。这个索引,称为元素的下标。

注意事项:数组中的元素下标是从0开始的。因此数组的下标范围是 [0, 长度-1]

如果,在访问数组元素的时候,使用了一个越界的下标,则会出现异常 ArrayIndexOutOfBoundsException

int[] array = new int[5];
array[0] = 10;					// 将数组中的第0个元素的值修改为10
System.out.println(array[3]);	// 输出数组中的第3个元素
array[5] = 100;					// ArrayIndexOutOfBoundsException
4.4.3、数组的下标分析

为什么数组的元素下标要从0开始?【了解】

数组是在堆上开辟的连续的空间,数组的变量中存储的其实是首元素的地址。在访问后面的元素的时候,其实是用首元素的地址,加上向后偏移的空间单位量来进行。因此首元素不需要进行任何的偏移,偏移量是0; 后面的一个元素,偏移量是1;以此类推。

因此数组的元素下标,可以认为是在进行元素访问的时候,需要使用首元素地址进行的偏移量。

4.4.4、遍历数组

遍历数组,其实就是因此获取到数组中的每一个元素。

1、使用下标遍历法

思路:数组的下标范围是[0, 长度-1],因此,我们可以使用循环,依次获取到每一个下标,再使用这个下标访问数组中的元素。

int[] arr = { 1, 2, 3, 4, 5 };
for (int i = 0; i < arr.length; i++) {
    // 通过下标,获取数组中的元素
    int ele = arr[i];
    System.out.println(ele);
}

2、使用增强for循环

int[] array = { 1, 2, 3, 4, 5 };

// 增强for循环
// 原理:依次将数组中的每一个元素,给ele赋值
for (int ele : array) {
    System.out.println(ele);
}

3、两种方式的不同点

1、下标遍历方式,在遍历的过程中,是可以获取到元素下标的。增强for循环中,没有下标的概念。
2、增强for循环中,不允许对数组的内容做修改。
3、增强for循环的遍历效率要高于下标遍历。
4.5、数组的排序

对数组中的元素,按照一定的大小关系,进行重新的排列。

4.5.1、常见的排序算法

冒泡排序、选择排序、快速排序、插入排序、堆排序、归并排序、希尔排序 …

4.5.2、冒泡排序

排序原理:依次比较两个相邻的元素,当满足交换的时候,交换这两个元素。

每一趟的比较,都从第0位开始,依次比较两个相邻的元素。

int[] array = { 10, 8, 6, 29, 11, 9, 3, 0, 18, 29 };

// 外层循环,控制比较发生了多少趟,数组长度-1趟
for (int i = 0; i < array.length - 1; i++) {
    // 内层循环,依次比较两个相邻的元素 i + j <= array.length - 2
    for (int j = 0; j < array.length - i - 1; j++) {
        // 比较两个相邻的元素
        if (array[j] > array[j + 1]) {
            int temp = array[j];
            array[j] = array[j + 1];
            array[j + 1] = temp;
        }
    }
}
4.5.3、选择排序

排序原理:固定一个下标,使用这个下标的元素,依次和后面的每一个元素进行比较。

public static void sort(int[] array) {
    for (int i = 0; i < array.length - 1; i++) {
        for (int j = i + 1; j < array.length; j++) {
            if (array[i] > array[j]) {
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    }
}
public static void sort(int[] array) {
    // 每一趟的比较,最多只需要发生一次交换即可。
    // 需要知道谁和谁交换
    for (int i = 0; i < array.length - 1; i++) {
        // 记录需要和第i位进行交换的下标
        // 对于升序排序,认为这一位对应的元素最小
        int swapIndex = i;
        for (int j = i + 1; j < array.length; j++) {
            // 内层循环,需要找出swapIndex位是谁
            // 对于升序排序,需要找出最小值的下标
            if (array[swapIndex] > array[j]) {
                swapIndex = j;
            }
        }
        
        if (swapIndex != i) {
            int tmp = array[swapIndex];
            array[swapIndex] = array[i];
            array[i] = tmp;
        }
    }
}
4.6、数组的查询

查询,其实就是从一个数组中查询指定元素所在的下标。如果需要查询的数据,在数组中不存在,一般情况话,会将查询结果设置为-1。

4.6.1、顺序查询

从第0个元素开始,依次使用每一个元素和要查询的元素进行比较。如果比较成立,则当前下标就是要查询的下标。其实顺序查询,就是遍历数组。

/**
 * 从数组array中,使用顺序查询,查询ele出现的下标
 * @param array 需要查询的数组
 * @param ele 需要查询的元素
 * @returns 查询的结果,如果不存在,返回-1
 */
public static int search(int[] array, int ele) {
    // 在这里,使用下标遍历更合适。因为我们就需要一个下标作为结果。
    for (int i = 0; i < array.length; i++) {
        if (array[i] == ele) {
            return i;
        }
    }
    // 如果循环结束了,方法还没结束,说明上方的比较,没有一个元素是匹配的
    // 也就是说,要查询的元素不存在
    return -1;
}
4.6.2、二分查询

在一个指定的范围内,查询数据。每次查询,取这个范围的一半。这样做可以将查询结果分为以下几种情况:

  • 如果中间的值,就是我们想要查询到,说明查询到了。
  • 如果中间的值,不是我们要查询的,可以将查询的范围缩减一半。

如果使用二分查询,查询数组中的元素下标,此时对数组是有要求的:

  • 数组需要是排序的状态。
/**
 * 从数组array中,使用二分查询,查询ele出现的下标
 * @param array 需要查询的数组
 * @param ele 需要查询的元素
 * @returns 查询的结果,如果不存在,返回-1
 */
public static int binarySearch(int[] array, int ele) {
    // 1、确定一个初始的查询范围
    int min = 0, max = array.length - 1;
    
    while (min <= max) {
        // 2、计算这个范围的中间下标
    	int mid = (min + max) / 2;
        
        // 3、用这个中间下标的元素和我们要查询的元素进行比较
    	if (array[mid] == ele) {
    	    // 说明找到了
    	    return mid;
    	}
    	else if (array[mid] > ele) {
    	    // 中间位的元素比要查询的元素大,说明元素在左侧
    	    // 修改新的区间
    	    max = mid - 1;
    	}
    	else {
    	    // 中间位的元素比要查询的元素小,说明元素在右侧
    	    // 修改新的区间
    	    min = mid + 1;
    	}
    }
    // 能执行到这里,说明区间已经缩小到不存在了,还没有找到指定的元素
    // 也就是说,要查询的元素在数组中不存在
    return -1;
}
4.7、数组拷贝

数组拷贝,分为浅拷贝和深拷贝。

4.7.1、浅拷贝

直接将一个数组的地址,赋值给另外的一个数组。这样拷贝出来的数组,拥有相同的地址,对任意的一个数组进行修改,都会影响到另外一个数组。

int[] array = new int[5];
// 这里,就是一个浅拷贝,target和array有着相同的内存地址,指向了相同的空间
int[] target = array;

array[0] = 100;
System.out.println(target[0]);	// 100
4.7.2、深拷贝

实例化一个新的数组,长度与原数组相同。让新的数组指向内存中的一块新的空间。然后将原数组中的每一个元素拷贝到目标数组中。这样目标数组和原数组有着相同的元素,但是他们是两个数组。对一个数组进行修改,不会影响到另外一个数组。

int[] array = new int[5];
int[] target = new int[array.length];

// 这里,将原数组中的每一个元素依次拷贝到目标数组中,是一个深拷贝。
for (int i = 0; i < array.length; i++) {
    target[i] = array[i];
}

array[0] = 100;
System.out.println(target[0]);	// 0
4.8、数组操作中常见的异常
4.8.1、ArrayIndexOutOfBoundsException

数组下标越界异常,常常发生于使用一个不存在的下标访问数组中的元素。

int[] array = new int[5];
array[-1] = 10;
array[5] = 10;
4.8.2、NullPointerException

空指针异常,常常发生于使用null来进行空间访问。

1、null是什么?

null表示空、没有。只能用于引用数据类型,表示这个引用变量中没有存储任何的地址,没有指向任何的一块空间。所有的引用数据类型的默认值都是null。

2、使用null进行空间访问,会出现空指针异常。

4.9、Arrays工具类

4.9.1、什么是工具类?

工具类,是一个类,这个类中包装了若干个用来方便完成指定功能的方法。这些方法称为工具方法。

工具方法,为了方便调用,一般都是静态方法。

4.9.2、Arrays

Arrays是一个用来操作数组的工具类

Arrays类在java.util包里面,因此,使用之前,需要先导包。

import java.util.Arrays;
// 或者
import java.util.*;
// 1、排序
Arrays.sort(array);

// 2、将一个数组中的元素拼接成一个字符串
System.out.println(Arrays.toString(array));

// 3、元素拷贝
// 3.1、将数组中从0位开始,拷贝指定数量的元素到一个新的数组中,并返回这个新的数组
//      在拷贝的时候,如果需要拷贝的元素数量比数组长度长,后面的部分填充默认值
int[] newArray = Arrays.copyOf(array, 5);
System.out.println(Arrays.toString(newArray));
// 3.2、将数组中指定范围 [from, to) 的元素拷贝到一个新的数组中,并返回
//     注意:from不能越界,但是to可以; from <= to
newArray = Arrays.copyOfRange(array, 5, 12);
System.out.println(Arrays.toString(newArray));

// 4、判断两个数组是否相同
System.out.println(Arrays.equals(array, newArray));

// 5、使用指定的值,填充这个数组
Arrays.fill(target, 20);
System.out.println(Arrays.toString(target));

// 6、使用二分查找法,查询某一个元素的下标
int index = Arrays.binarySearch(array, 7);
System.out.println(index);
4.9.3、System.arraycopy

是System类中的一个方法,不开源。

// 将一个数组的指定位开始的元素,拷贝到目标数组的指定位
// src: 原数组,需要从这里拷贝元素
// srcPos: 从原数组的这一位开始拷贝元素
// dest: 目标数组,将元素拷贝到这个数组中
// destPos: 将元素从这个位置开始覆盖
// length: 拷贝的元素的数量
// System.arraycopy(src, srcPos, dest, destPos, length);

示例:

int[] src = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] dst = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };

System.arraycopy(src, 3, dst, 5, 4);
System.out.println(Arrays.toString(dst));
// 输出结果:[0, 10, 20, 30, 40, 3, 4, 5, 6, 90, 100]
4.10、二维数组

数组中的元素,是一个小的数组。数组嵌套数组。

// 声明了一个二维数组,数组array中存储的元素,是一个数组。
// 数组嵌套数组。
int[][] array;
4.10.1、二维数组的实例化

和一维数组是一样的。

// 1. 实例化的时候,在第一个中括号里面写上一个长度。
//    代表了二维数组的长度
//    数组array的长度是5,里面有5个元素,每一个元素都是一个int[]。
//    数组实例化完成后,里面的元素填充的是默认值,而二维数组中填充的是 null 。
int[][] array = new int[5][];
System.out.println(array[0]);		// null

// 2. 实例化的时候,在每一个中括号里面定义一个长度
//    第一个中括号中的长度,是数组array的长度。
//    第二个中括号中的数字,数组array中存储的元素,默认是 new int[3]。
int[][] array = new int[5][3];
4.10.2、二维数组的使用
// 二维数组的元素访问,和一维数组相同,都是通过下标的方式访问的。
int[][] array = new int[5][3];
// array[0]是int[]类型的。
// 访问数组array中的第0个元素(是一个小数组)的第0个元素。
array[0][0] = 10;
4.11、关键字 …

在方法的参数定义中,可以使用 数据类型... 来表示一个数组。

public static void show(int... params) {
    // 这个方法的中的参数params,其实就是一个 int[]
}

…作为参数,有什么特点:

1、这种方式定义的参数,在调用方法的时候,可以直接数组中的每一个元素直接写在实参列表中。

// 当数组作为参数,最传统的传参方式。
show(new int[]{1, 2, 3, 4, 5});

// 如果参数是int...,可以直接将数组中的所有元素写到参数列表中。
show(1, 2, 3, 4, 5);

2、这种方式定义的参数,必须放到参数列表的最后位。

标的方式访问的。
int[][] array = new int[5][3];
// array[0]是int[]类型的。
// 访问数组array中的第0个元素(是一个小数组)的第0个元素。
array[0][0] = 10;




#### 4.11、关键字 ...

在方法的参数定义中,可以使用 `数据类型...` 来表示一个数组。

```java
public static void show(int... params) {
    // 这个方法的中的参数params,其实就是一个 int[]
}

…作为参数,有什么特点:

1、这种方式定义的参数,在调用方法的时候,可以直接数组中的每一个元素直接写在实参列表中。

// 当数组作为参数,最传统的传参方式。
show(new int[]{1, 2, 3, 4, 5});

// 如果参数是int...,可以直接将数组中的所有元素写到参数列表中。
show(1, 2, 3, 4, 5);

2、这种方式定义的参数,必须放到参数列表的最后位。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值