数组的概述
在Java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常快速。但是为这种速度所付出代价是数组对象大小被固定,并且在其声明周期中不可改变。数组内的元素既可以是基本数据类型,也可以是引用数据类型。区别在于对象数据保存的是引用,基本类型数组直接保存基本类型的值。
- java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是Object。
- 数组实际上是一个容器,可以同时容纳多个元素(数组是一个数据的集合)字面意思:“一组数据”
- 数组当中可以储存“基本数据类型”的数据,也可以储存“引用数据类型”的数据
- 数组因为是引用类型,所以数组对象是堆内存当中(数组是存储在堆当中的)。
- 数组当中如果存储的是“java对象”的话,实际上存储的是对象的“引用地址”
- 数组一旦创建,在java中规定,长度不可变。(数组长度不可变)
- 数组的分类: 一维数组、二维数组、三维数组、多维数组…(一维数组较多,二维数组偶尔使用)
- 所有的数组对象都有length属性(java自带的),用来获取数组中元素的个数。
- java中的数组要求数组中元素的类型统一,比如int类型数组只能存储int类型,String类型数组只能存储String类型。
- 数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的,内存地址连续这是数组内存元素的特点,数组实际上是一种简单的数据结构。
- 所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。(数组中首元素的内存地址作为整个数组对象的内存地址)
- 对于数组来说,实际上只能储存java对象的“内存地址”,数组中存储的每个元素是"引用".
优点:
查询/查找/检索某个下标上的元素时效率极高,可以说时查询效率最高的一个数据结构。
为什么检索高??
1.每一个元素的内存地址在空间存储上是连续的。
2.每一个元素类型相同,所以占用空间大小一样。
3.知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址,通过内存地址定位元素,所以数组的检索效率是最高的。(int类型的数据占用4个字节)
缺点:
1.由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
2.数组不能存储大数据量,因为很难在内存空间找到一块特别大的连续的内存空间。
数组的定义方式
使用new操作符来创建数组,语法如下:
arrayRefVar = new dataType[arraySize];
上面的语法语句做了两件事:
- 一、使用 dataType[arraySize] 创建了一个数组。
- 二、把新创建的数组的引用赋值给变量 arrayRefVar。
数组变量的声明,和创建数组可以用一条语句完成,如下所示:
dataType[] arrayRefVar = new dataType[arraySize];
另外,你还可以使用如下的方式创建数组。
dataType[] arrayRefVar = {value0, value1, ..., valuek};
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
定义一个数组类型的变量,使用数组类型“类型[]”,例如,int[]
。和单个基本类型变量不同,数组变量初始化必须使用new int[5]
表示创建一个可容纳5个int
元素的数组。
Java的数组有几个特点:
- 数组所有元素初始化为默认值,整型都是
0
,浮点型是0.0
,布尔型是false
; - 数组一旦创建后,大小就不可改变。
要访问数组中的某一个元素,需要使用索引。数组索引从0
开始,例如,5个元素的数组,索引范围是0
~4
。
可以修改数组中的某一个元素,使用赋值语句,例如,ns[1] = 79;
。
可以用数组变量.length
获取数组大小
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-il5kI5j2-1660981621008)(D:\Typora\img\image-20220815100037545.png)]
常用的数组操作
遍历
通过for
循环就可以遍历数组。因为数组的每个元素都可以通过索引来访问,因此,使用标准的for
循环可以完成一个数组的遍历:
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int i=0; i<ns.length; i++) {
int n = ns[i];
System.out.println(n);
}
}
}
为了实现for
循环遍历,初始条件为i=0
,因为索引总是从0
开始,继续循环的条件为i<ns.length
,因为当i=ns.length
时,i
已经超出了索引范围(索引范围是0
~ ns.length-1
),每次循环后,i++
。
第二种方式是使用for each
循环,直接迭代数组的每个元素:
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
}
}
在for (int n : ns)
循环中,变量n
直接拿到ns
数组的元素,而不是索引。
显然for each
循环更加简洁。但是,for each
循环无法拿到数组的索引
排序
常用的排序算法有冒泡排序、插入排序和快速排序等。
冒泡排序
使用冒泡排序算法对一个整型数组从小到大进行排序:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
// 排序前:
System.out.println(Arrays.toString(ns));
for (int i = 0; i < ns.length - 1; i++) {
for (int j = 0; j < ns.length - i - 1; j++) {
if (ns[j] > ns[j+1]) {
// 交换ns[j]和ns[j+1]:
int tmp = ns[j];
ns[j] = ns[j+1];
ns[j+1] = tmp;
}
}
}
// 排序后:
System.out.println(Arrays.toString(ns));
}
}
冒泡排序的特点是,每一轮循环后,最大的一个数被交换到末尾,因此,下一轮循环就可以“刨除”最后的数,每一轮循环都比上一轮循环的结束位置靠前一位。
选择排序
import java.util.Arrays;
public class Array_01 {
public static void main(String[] args) {
// bubblesort();
selectsort();
// insertSort();
// countSort();
}
private static void selectsort() {
int[] arr = {23, 45, 6, 7, 99, 78, 29};
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
swap(arr, i, j);
}
}
// System.out.println(Arrays.toString(arr));
}
System.out.println(Arrays.toString(arr));
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
}
}
插入排序
import java.util.Arrays;
public class Array_01 {
public static void main(String[] args) {
// bubblesort();
// selectsort();
insertSort();
// countSort();
}
private static void insertSort() {
int[] arr = {34, 56, 8, 12, 3, 45, 98};
int e;
int j;
for (int i = 1; i < arr.length; i++) {
e = arr[i];
for (j = i; j > 0 && arr[j - 1] > e; j--) {
arr[j] = arr[j - 1];
}
arr[j] = e;
System.out.println(Arrays.toString(arr));
}
// System.out.println(Arrays.toString(arr));
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
}
}
计数排序
import java.util.Arrays;
public class Array_01 {
public static void main(String[] args) {
// bubblesort();
// selectsort();
// insertSort();
countSort();
}
private static void countSort() {
int[] arr = {3, 67, 3, 5, 46, 89, 1, 47};
int min = arr[0];
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
int[] nums = new int[max - min + 1];
int offset = min;
for (int i = 0; i < arr.length; i++) {
nums[arr[i] - offset]++;
}
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
for (int j = 0; j < nums[i]; j++) {
arr[index++] = i + offset;
}
}
}
System.out.println(Arrays.toString(nums));
System.out.println(Arrays.toString(arr));
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
}
}
查找
顺序查找和二分法查找
public class Array_03 {
public static void main(String[] args) {
linearSearch();
binarySearch();
}
private static void linearSearch(){
int[] arr = {6,9,1,5,2,3,8,4,7};
int key = 8;
int index = -1;
for (int i = 0; i <arr.length; i++){
if (arr[i] == key){
index = i;
break;
}
}
System.out.println(index);
}
private static void binarySearch(){
int[] arr = {6,9,1,5,2,3,8,4,7};
int key = 8;
int min_index = 0;
int max_index = arr.length - 1;
int mid_index = (min_index + max_index)/ 2 ;
while (arr[mid_index] != key) {
if (key < arr[mid_index]) {
max_index = mid_index - 1;
}
if (arr[mid_index] < key) {
min_index = mid_index -1;
}
if (min_index>max_index){
mid_index = -1;
break;
}
mid_index = (min_index+max_index)/2;
}
System.out.println(mid_index);
}
}
二维数组
二维数组就是数组的数组。定义一个二维数组如下:
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(ns.length); // 3
}
}
访问二维数组的某个元素需要使用array[row][col]
,例如:
System.out.println(ns[1][2]); // 7
要打印一个二维数组,可以使用两层嵌套的for循环:
for (int[] arr : ns) {
for (int n : arr) {
System.out.print(n);
System.out.print(', ');
}
System.out.println();
}
或者使用Java标准库的Arrays.deepToString()
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(Arrays.deepToString(ns));
}
}
Arrays数组工具类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
具有以下功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
具体说明请查看下表:
序号 | 方法和说明 |
---|---|
1 | public static int binarySearch(Object[] a, Object key) 用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。 |
2 | public static boolean equals(long[] a, long[] a2) 如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
3 | public static void fill(int[] a, int val) 将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
4 | public static void sort(Object[] a) 对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
5 | public static int[] copyOf(int[] original, int[] newLength ) 复制指定的数组,截取或用0填充,以使副本具有指定的长度 |
6 | public static toString(int[] a) 返回指定数组内容的字符串表现形式 |