1.写在前面
我们在了解/学习一个新技能之前,都会先了解这个技能的概念。搞清楚是用来干嘛的,能解决什么问题、这样的话,方便后面正确的打开,磨刀不误砍柴工。
同时,数组不仅仅是某一编程语言中的数据类型,它还是一种最基础的数据结构。
2.数组的概念(维基百科):
数组数据结构(array data structure),简称数组(Array),是由相同类型的元素(element)的集合所组成的数据结构,分配一块连续的内存来存储。利用元素的索引(index)可以计算出该元素对应的存储地址。(这个地方思考一个问题,数组为什么可以随机访问?)
大白话解释一下:
数组是一种数据结构,只能存储同一类型值。意思就是,你创建了一个String类型的数组,那么你这个数组只能存储String 类型的数据,放其他的要报错。
2.1怎么访问数组中的数据呢?
可以通过下标访问数组中的每一个值,比如有这样一个数组:
int [] intArray = {25,14,5,3,6};我们画个图
看这个图,intArray[2] 访问的就是 intArray 数组中 5 这个元素,下标是从0开始的。
通过下标,我们能访问数组中每一个元素。
3.数组的特点:
- 定长:数组的长度一旦确定就不能修改
- 创建数组时会开辟一块连续的内存来存储
- 存取元素的速度快,可以通过索引,快速定位到任意一个元素
关于通过索引快速定位到一个元素的原理,我在一文看懂ArrayList的自动扩容这篇文章中有说明。感兴趣的话,可以看看。
3.1定长怎么理解呢?
就是说:你创建数组的时候,若初始化数组的长度为5,那么你只能往数组中插入5个元素,你往里面插入第六个元素的时候,会提示你溢出的错误。5个元素,那下标就是[0,4] 若你试图访问a[5] 这个元素,程序会引发“array index out of bounds” 异常而终止执行。
4.数组的声明与初始化
4.1 声明
// 声明语法1:数据类型[] 数组名
int[] arr;
// 声明语法2:数据类型[] 数组名
int arr[];
这两种风格你喜欢哪种呢?个人比较喜欢第一种风格,因为它将类型int[] (整型组数) 与变量名arr 分开了,看上去更加清晰。
4.2初始化
4.2.1静态初始化:
// 方式一:数据类型[] 数组名 = {元素1,元素2,元素3,...}
int[] arr = {1,2,3,4};
// 方式二:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};
int[] arr = new int[]{1,2,3,4};
4.2.2动态初始化:
//数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
int[] arr = new int[5]
4.3数组元素的访问
4.3.1对于数组来说,我们通过元素的索引访问,那什么是索引呢?
索引:前面我们有提到创建数组,内存会分配一块连续的空间来存储,第一个元素所在内存的地址称为基地址,那么第一个元素相对于基地址来说,偏移量为0,所以索引是从0开始,那么第二个地址与基地址之间就偏移了1. 因此,索引的范围为:[0,数组的长度-1] .
利用元素的索引(index)可以计算出该元素对应的存储地址,
通过 array.length 可以获得数组中元素的个数。
4.3.2数组的遍历
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印数组的属性,输出结果是5
System.out.println("数组的长度:" + arr.length);
//遍历输出数组中的元素
System.out.println("数组的元素有:");
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
}
4.3.3数组元素的默认值
当我们动态初始化数组时:
//数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
int[] arr = new int[5]
此时只确定了数组的长度,那么数组的元素是什么值呢?
数组元素类型 | 元素默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
Long | 0L |
float | 0.0F |
double | 0.0 |
char | 0或写为:‘\u0000’(表现为空) |
引用类型 | null |
4.4数组的拷贝
在Java中,可以将一个数组变量拷贝给另一个数组变量,这个时候,两个变量引用的是同一个数组。
int[] firstArray = {1,2,3,4,5};
int[] secondArray = firstArray ;
这个时候,secondArray 引用的就是 firstArray 的地址,这两个数组是一样的。
那如果 希望将一个数组的所有值拷贝到一个新的数组中去呢?
4.4.1全新拷贝
Arrays类的copyOf 方法:
int[] copiedArray=Arrays.copyOf(firstArray ,firstArray .length);
第一个参数是要拷贝的数组。第二个参数是数组的长度
Arrays.copyOf 这个方法通常用来增加数组的大小。比如
int[] copiedArray=Arrays.copyOf(firstArray ,2 * firstArray .length);
拷贝firstArray 数组,并且将数组扩大为firstArray 数组大小的两倍.
那么问题来了,多余的元素的默认值是什么呢?
int 类型的就是0, 布尔类型的就是false.
相反: 如果长度小于原始数组的长度呢?
这种请跨的话,只拷贝最前面的数据元素。
比如元素数组的长度为10,拷贝时你传的参数是5,那就只拷贝原始数组的前5个元素。
4.5数组的排序
若对数值型数组进行排序,则使用Arrays 类中的sort 方法。
int[] firstArray = {9,58,3,91,5};
Arrays.sort(firstArray);
Arrays.sort() 使用了优化的快速排序方法,快排算法对于大多数数据集合来说都是效率比较高的。
5.数据的下标为什么从0开始?
我们之前在概念中提到:利用元素的索引(index)可以计算出该元素对应的存储地址。时,让大家思考一个问题,为什么数据可以随机访问呢?主要就是以下两点:
- 数组是一种线性结构
- 连续的内存空间和相同的数据结构
计算机会给数组分配连续的内存空间,这块内存有一个首地址,我们访问数组的某个元素的时候,会通过一个寻址公式
来计算存储的地址,公式:arr[i]_address = base_address + i * data_type_size(arr[i] 首地址 = 数组内存块首地址 + 数据类型大小 * i ,其中i为偏移量。)
baseaddress:内存块的首地址。datatype_size:数组中每个元素的大小,如:每个元素大小是4个字节,我们都知道,是通过元素的地址来访问内存中的数据,那么,我们就可以根据首地址 + 偏移量 来随机访问每个元素啦,数组中的下标就是偏移量,第一个元素就是首地址,偏移量为0,所以数据下标从0开始, ,根据下标和首地址,数据就可以随机访问数组中的元素。