数组是相同类型元素的集合。创建数组的时候,Java就会再内存中分配一段连续的空间来存放数组的内容。每一个数组内容都有自己的编号,这个编号从0开始的,也就是数组的下标从0开始。通过数组的下标来访问数组的内容。
创建一个数组:
T[] 数组名 = new T[N];
T:数组中元素的类型。
T[]:表示是数组类型,[]也可以放在数组名的后面。这里的[]不能有任何的数组
N:创建数组要的长度。
比如:
int[] array1 = new int[10]; // 创建一个可以容纳10个int类型元素的数组
double[] array2 = new double[5]; // 创建一个可以容纳5个double类型元素的数组
String[] array3 = new double[3]; // 创建一个可以容纳3个字符串元素的数组
要对数组进行初始化,有两种方式。
静态初始化:知道数组的内容,就可以静态初始化。
int[] arr = { 1,2,3,4 }; //省略情况
int[] arr = new int[] { 1,2,3,4 };//可以省略new int[]
上面两种初始化的方式是没有区别的。
如果不知道数组的内容,那么就可以使用动态初始化。
int[] arr = new int[10]; //定义10个int类型的数组
上面两种创建方式都可以分两步进行创建:
int[] array1;
array1 = new int[10];
int[] array2;
array2 = new int[]{10, 20, 30};
// 注意省略格式不可以拆分, 否则编译失败
// int[] array3;
// array3 = {1, 2, 3};
如果没有对数组进行初始化,数组中的元素有默认值。不同的数据类型有不同的默认值,如果是简单类型,大多是默认值为0。float是0.0f,double是0.0,char是/u0000,boolean是false;引用类型比如字符串类型则是null。
数组在内存中是一段连续的空间,下标是从0开始的。比如:
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
当然,我们可以对数组的内容进行修改。将上面的array[0]改为20:
array[0] = 20;
System.out.println(array[0]);
在Java中,数组[]里面的大小支持常量,变量和表达式。
这在C语言中,C99之前是不行的。
数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素。数组的下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
int[] array = {1, 2, 3};
System.out.println(array[3]); // 数组中只有3个元素,下标一次为:0 1 2,array[3]下标越界
上面的结果报出了一个数组越界的异常。当下标是3的时候,说明越界了。这样的错误发生在main函数的第12行。通过这个,可以很快的找到问题的所在。
在Java中,对数组的遍历有两种方式:第一种是for,第二种是增强for(foreach)。
由于Java是面向对象的语言,在Java中,一切都是对象,比如数组。数组这个对象有它的属性,比如数组的长度等,基于这一点,我们可以不用刻意知道数组的长度,使用.length来获取数组的长度。
//for
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
//foreach
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
//说明:
//int x :array
//数组中的数据类型+变量名,这里array数组里面的类型是int
//array是数组名
//foreach
double[] array = {1.0, 2.0, 3.0};
for (double x : array) {
System.out.println(x);
}
增强for循环是得不到数组的下标的,所以它一般用于数组的遍历,比如数组累加这样的操作。
要了解数组在内存里面的情况,就要知道Java内存分布是什么样的,这里做必要的介绍。Java经过编译后产生的.class文件被加载到JVM虚拟机里面的本地方法栈里面运行。为了高效管理内存,JVM对内存进行了划分。JVM是一个软件,由C/C++编写的软件。这是因为系统之类的由C/C++代码编写比较高效。
说明:
(1)Java虚拟机栈:也是我们在Java中常说栈。一般用来存放局部变量。
(2)本地方法栈:运行由c/c++编写的代码。经过编译产生的.class文件被加载到这里运行。
(3)堆:是Java中最大的一块内存空间。只要是对象,都是在堆上开辟的。在堆里面的引用叫对象。堆上的内存是不需要进行手动释放的,Java有自己的垃圾回收机制。
(4)程序计数器:保存一些指令和信息等。
(5)方法区:存放常量,静态成员变量等。
这里重点关心堆和虚拟机栈。
引用类型创建的变量,一般称为对象的引用。存储的是对象所在空间的地址。如何理解数组的引用呢?比如写了这么一个代码:
int[] arr = new int[] {1,2,3};
在内存中发生了什么呢?
这里的arr是局部变量,运行代码后,java就会在栈上为arr分配空间。后面new了之后,就会在堆上开辟一块空间来保存这些内容。arr存的是后面对象的地址。这里要强调的是,引用变量存的是对象的地址。
现在我们直接打印arr看看:
打印出了这么一个地址(可以理解为数组首元素地址)。这里的地址为了安全,经过了特殊的处理,是这个引用对象的哈希地址。@之后的是哈希地址,@前的I表示这是一个int型,[表示是一个数组。arr这个引用,引用(指向)了一个数组对象。在比如以下代码:
int[] arr = new int[] { 1,2,3,4 };
int[] brr = arr;//???
System.out.println(arr);
System.out.println(brr);
这里打印出来的地址是一样的,说明brr也引用了与arr相同的对象。
现在我们打印出这两个数组的内容看看:
在brr里面修改brr[0]的值:
要注意这里的brr=arr,是把arr的值给了brr。arr引用了这个对象,brr引用了arr引用的对象,而不是brr指向arr(换句话来说,就是引用指向引用)。
那么要注意的几点:
(1)引用不能指向引用,引用指向了对象(引用的对象)。
(2)一个引用,不能同时引用多个对象。
这里的arr引用新的对象的时候,没有同时引用。
(3)引用不一定在栈上。还有可能在堆上,比如成员变量。
(4)引用赋值为null表示不指向任何对象。
public static void fun1(int[] array) {
array = new int[] { 5,6,7,8 };
}
public static void fun2(int[] array) {
array[0] = 2;
}
public static void main(String[] args) {
int[] arr = new int[] { 1,2,3,4 };
fun1(arr);
System.out.println(Arrays.toString(arr));
int[] brr = new int[] { 1,1,1,1 };
fun2(arr);
System.out.println(Arrays.toString(arr));
}
分别打印出来的结果是?
第一个的图示:
第二个和之前的是相同的。
二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组。
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
int[][] arr1 = { {1,2,3},{4,5,6} };
int[][] arr2 = new int[][] { {1,2,3},{4,5,6} };
int[][] arr3 = new int[2][3];
int[][] arr4 = new int[2][];
在Java中,如果是前两种定义的方式,必须要手动加括号。同时,行是必须要指定的,列可以不用指定。
和一维数组类似,二维数组有for循环和foreach循环来进行遍历。
int[][] arr1 = { {1,2,3},{4,5,6} };
//for
for (int i = 0; i < arr1.length; i++) {
for (int j = 0; j < arr1[i].length; j++) {
System.out.print(arr1[i][j] + " ");
}
System.out.println();
}
//foreach
for (int[] x : arr1) {
for (int y : x) {
System.out.print(y + " ");
}
System.out.println();
}
Java中的Arrays工具类包括了数组常用的一些方法,比如数组的排序,数组的拷贝等方法,而这些方法,在我们的日常生活中也是经常使用的。在使用之前需要导入:import java.util.Arrays;
这些方法是重构的,这里拿int类型的数组简单介绍一下,其他的原理是相同的。具体的内容见官方文档。
将一维数组变成字符串。二维数组使用Arrays.deepToString方法。
public static String toString(int[] a)
参数
a
- 要返回其字符串表示形式的数组
public static void main(String[] args) {
int[] array = new int[] { 1,2,3,4 };
System.out.println(Arrays.toString(array));//[1,2,3,4]
}
从0下标来时复制指定长度数组的内容到新的数组中。
public static int[] copyOf(int[] original, int newLength)
参数
original
- 要复制的数组
newLength
- 要返回的副本的长度
public static void main(String[] args) {
int[] array = new int[] { 1,2,3,4 };
int[] ret = Arrays.copyOf(array,2);
System.out.println(Arrays.toString(ret));//[1,2]
}
和Arrays.copfOf方法类似,这个是指定范围的拷贝。范围是左闭右开区间。
public static int[] copyOfRange(int[] original, int from, int to)
参数
original
- 要从中复制范围的数组
from
- 要复制的范围的初始索引(包括)
to
- 要复制的范围的最终索引,除了它本身。 (该索引可能位于数组之外)
public static void main(String[] args) {
int[] array = new int[] { 1,2,3,4 };
int[] ret = Arrays.copyOfRange(array,2,4);
System.out.println(Arrays.toString(ret));//[3,4]
}
对数组进行升序排序。
public static void sort(int[] a)
public static void main(String[] args) {
int[] array = new int[] { 4,3,2,1 };
Arrays.sort(array);
System.out.println(Arrays.toString(array));//[1,2,3,4]
}
对数组内容进行指定填充。
public static void fill(int[] a, int val)//全部填充
参数
a
- 要填充的数组
val
- 要存储在数组的所有元素中的值public static void fill(int[] a, int fromIndex, int toIndex, int val)//指定范围填充
参数
a
- 要填充的数组
fromIndex
- 要用指定值填充的第一个元素(包括)的索引
toIndex
- 要用指定值填充的最后一个元素(排除)的索引
val
- 要存储在数组的所有元素中的值
public static void main(String[] args) {
int[] array = new int[10];
Arrays.fill(array,10);
Arrays.fill(array,2,8,20);
System.out.println(Arrays.toString(array));
}
判断两个数组的内容是否相同
public static boolean equals(int[] a, int[] a2)
参数
a
- 要测试相等的一个数组
a2
- 要测试的其他数组是否相等
public static void main(String[] args) {
int[] arr1 = new int[] { 4,3,2,1 };
int[] arr2 = new int[] { 1,2,3,4 };
int[] arr3 = new int[] { 1,2,3,4 };
System.out.println(Arrays.equals(arr1, arr2));//false
System.out.println(Arrays.equals(arr2, arr3));//true
}
二分查找数组内容,使用前先sort排序数组。
public static int binarySearch(int[] a, int fromIndex, int toIndex, int key)
参数
a
- 要搜索的数组
fromIndex
- 要搜索的第一个元素(包括)的索引
toIndex
- 要搜索的最后一个元素(排他)的索引
key
- 要搜索的值
public static void main(String[] args) {
int[] arr1 = new int[] { 4,3,2,1 };
System.out.println(Arrays.binarySearch(arr1, 3));//下标是1
}