文章目录
Java数组学习
数组的定义
- 数组是相同类型数据的有序集合
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们
数组的声明创建
-
首先必须声明数组变量,才能在程序中使用数组。
dataType[] arrayRefVar; // 首选方法 或 dataType arrayRefVar[];// 效果相同,但不是首选方法,C/C++风格
-
Java语言使用new操作符来创建数组,语法如下:
dataType[] arrayRefVar = new dataType[arraySize];
-
数组的元素是通过索引访问的,数组索引从0开始。
-
获取数组长度:
属性方法:arrays.length
数组的类型
数组是一个对象,不同类型的数组具有不同的类
@Test
public void test0() {
int[] a = new int[]{};
System.out.println(a.getClass());
double[] b = new double[]{};
System.out.println(b.getClass());
}
class [I
class [D
Process finished with exit code 0
内存分析
-
数组声明后,会在栈中占用一个空间来存放这个数组引用对象的地址
-
当数组初始化创建后,会根据数组的大小在堆中开辟对应大小若干个大小的内存空间来存放数组的数值。
三种初始化
静态初始化
利用数组初始化器,创建 + 赋值,数组大小自适应
// 基本类型数组
int[] a = new int[]{1, 2, 3};
// 可以省略new int[]
int[] a = {1, 2, 3};
// 引用类型数组,Man是创造的类
Man[] mans = new Man[]{new Man(1, 1), new Man(2, 2)};
Man[] mans = {new Man(1, 1), new Man(2, 2)};
注意:静态初始化不能申明数组的大小,否则会报错,申明数组大小属于动态初始化。
错误:int[] a = int[3]{1,2,3}
介于静态和动态初始化之间(C#下是可以的)
数组new关键字的使用与否
不同于String类,String由于实现了常量池 所以new 和不new 有区别:new的话,引用变量指向堆区。不new的话,引用变量指向常量池。
对于数组的定义,初始化时用new与不用new 没区别 ,只是两种方式罢了,因为数组是引用数据类型,建立对象时,无论用不用new,数组实体都是放在 堆内存中,引用变量放在栈内存。
省略new被称为类型推断
// 正确
int[] a;
a = new int[]{1,2,3};
// 正确
int[] a = {1, 2, 3}
// 错误
int[] a;
a = {1, 2, 3} // 无new下,同一行下无类型,无法推断处类型
动态初始化
创建,需要在创建时加入数组的大小,而申明引用类型时,不能加数组大小。然后自己分别赋值
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
不管是动态初始化还是静态初始化,一旦初始化完成,数组的大小就确定了。
默认初始化
数组创建后动态初始化后未赋值
与实例变量相似,数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
比如:创造的数组是整数数组,其默认初始化后的初始值就是相当于整数实例变量的数值,整数实例变量的默认初始值应该为0,所以整数数组中元素的默认初始值也为0
package com.slipperySoap.arrays;
public class Demon01 {
// 类变量
static int a;
// 实例变量
int d;
public static void main(String[] args) {
System.out.println("a = " + a);
Demon01 demon01 = new Demon01();
System.out.println("d = " + demon01.d);
int[] b = new int[10];
for(int c:b){
System.out.print(c + " ");
}
}
}
a = 0
d = 0
0 0 0 0 0 0 0 0 0 0
Process finished with exit code 0
数组的四个基本特点
定长
其长度是确定的,数组一旦被创建,它的大小就是不可以改变的。
同类型
其元素类型必须是相同类型的,不允许出先混合类型。
任意类型
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
对象性
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于对该对象的成员变量,数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身就是在堆中的。
数组边界
下标的合法区间:[0, length - 1], 如果越界就会报错
数组的使用
-
for-Each 循环
-
数组作方法入参
-
数组作返回值
for-each循环
for-each循环即增强型for循环, IDEA中输入foreach能快速生成增强型for循环的框架
int[] arrays = {1, 2, 3, 4, 5};
// JDK1.5以上,没有下标
// for-each即增强型for循环
for(int array : arrays){
System.out.println(array);
}
1
2
3
4
5
Process finished with exit code 0
数组作方法入参
数组作为引用对象,其变量存在栈中,可以作为参数进行参数传递。
因为数组作为引用对象,其对象存在于堆中,所以可以在方法中直接对堆中的值进行操作。(与C语言中的指针相似)
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
printArrays(arrays);
addUpArrays(arrays, 5);
printArrays(arrays);
}
public static void printArrays(int[] arrays){
for(int i = 0; i < arrays.length; i++){
System.out.print(arrays[i] + " ");
}
System.out.println();
}
public static void addUpArrays(int[] arrays, int x) {
for(int i = 0; i < arrays.length; i++){
arrays[i] += x;
}
}
1 2 3 4 5
6 7 8 9 10
Process finished with exit code 0
数组作返回值
数组的变量值能作为地址返回,所以可以定义方法数组的引用类型,返回这个地址值后赋值给新的数组变量。
public static void main(String[] args) {
int[] arrays = {1, 2, 3, 4, 5};
printArrays(arrays);
int[] reverse = reverseArrays(arrays);
printArrays(reverse);
}
public static int[] reverseArrays(int[] arrays){
int[] reverseArrays = new int[arrays.length];
for(int i = 0; i < arrays.length; i++){
reverseArrays[arrays.length-i-1] = arrays[i];
}
return reverseArrays;
}
public static void printArrays(int[] arrays){
for (int i:
arrays) {
System.out.print(i + " ");
}
System.out.println();
}
1 2 3 4 5
5 4 3 2 1
Process finished with exit code 0
多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一堆数组,其每一个元素都是一个一维数组,所以在动态创建多维数组的时候,可以只规定第一维数组的大小。
二维数组的初始化
(静态初始化,动态初始化注意点和一维数组一样)
动态初始化
// 正规
int[][] a = new int[2][5];
或
// 类C
int a[][] = new int[2][5];
或
// 介于二者之间的稀奇古怪写法
int[] a[] = new int[2][5];
// 特殊的一维数组的体现, 此时遍历的时候会遍历空指针
int[][] a = new int[2][];
int[][][] a = new int[2][][];
int[][][][] a = new int[2][][][];
int[][] a[][] = new int[2][][][];
int[][][] a[] = new int[2][][][];
注意:对于多维数组,只要确定第一维的长度即可,因为存储的时候我们是一维存储的方式,多维数组可以理解为多个数组的嵌套
int[][] ints = new int[100][100];
for(int i = 0; i < ints.length; ++i) {
Arrays.fill(ints[i], 123);
}
for(int i = 0; i < ints.length; ++i) {
System.out.println(Arrays.toString(ints[i]));
}
静态初始化
多维数组中的静态初始化区别于动态初始化数组中的默认初始化,静态初始化没有默认初始化。
多维数组中存在锯齿数组,即每行的长度可以不同, 这也是多维数组是特殊的一维数组的体现
int[][] a = new int[][] {{1,2},{1,2,3}};
int[][] a = {{1,2},{1,2,3,4}}; // 类型推断
int[][] a;
a = new int[][]{{1,2},{1,2,3,4,5}};// 类型推断
package com.slipperySoap.arrays;
public class Demon05 {
public static void main(String[] args) {
// 静态初始化
int[] a1[] = new int[][]{{1, 2, 3}, {2, 3}};
// 类型推断
int[][] a2 = {{1, 2, 3}, {2, 3}};
int[] a3[];
a3 = new int[][]{{1, 2, 3}, {2, 3}};
// 动态初始化
int[][] b1 = new int[4][4];
int[][] b2[] = new int[2][][];
printArrays(a1);
printArrays(a2);
printArrays(a3);
printArrays(b1);
System.out.println(b2[0]);
}
public static void printArrays(int[][] arrays){
// for (int x[]:
// arrays) {
// for (int y:
// x) {
// System.out.print(y + " ");
// }
// System.out.println();
// }
for (int i = 0; i < arrays.length; i++) {
if(arrays[i] == null){
System.out.println("null");
continue;
}
for (int j = 0; j < arrays[i].length; j++) {
System.out.printf("%d ", arrays[i][j]);
}
System.out.println();
}
System.out.println();
}
}
1 2 3
2 3
1 2 3
2 3
1 2 3
2 3
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
null
Process finished with exit code 0
多维数组内存分析
和一维数组类似,数组作为引用类型,变量名作为根地址存放在栈中,只是每一维存取了下一维地址到堆中,依次类推,最后一维为对应类型的数值,存放在堆中。
Arrays工具类
-
数组的工具类java.util.Arrays
-
由于数组对象本身并没有什么方法可以供我们调用(除了Arrays.length属性方法等少数方法能用),但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
-
查看JDK帮助文档
-
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而不用使用对象来调用(注意:是不用,不是不能)
-
常用功能:
- 给数组赋值:通过fill方法
- 对数组排序:通过sort方法,按升序
- 比较数组:通过equals方法比较数组中元素值是否相等
- 查找数组元素:通过binarySearch方法本办法能对排序好的数组进行二分查找法操作。
- 还有很多方法,可以多看API下java.util.Arrays包的内容
equals()
boolean equals(int[] a,int[] b):判断两个数组内容是否相等—数组是有序的,所以是对应位置下标的数组相等。
// equals()
@Test
public void test0(){
int[] a1 = new int[]{1,2,3,4,5};
int[] a2 = new int[]{5,4,3,2,1};
int[] a3 = new int[]{1,2,3,4,5};
System.out.println(Arrays.equals(a1,a2));
System.out.println(Arrays.equals(a1,a3));
}
false
true
Process finished with exit code 0
-
public static boolean equals(Object[] a, Object[] a2)
对象数组的比较,底层调用对象数组的equals方法
toString()
String toString(int[] a):返回一个将对应类型数组的元素按照其值转化为以逗号空格为间隔的放在中括号[]中的字符串。
int[] arrays = {1, 2, 3, 4, 5};
System.out.println(arrays);
System.out.println(Arrays.toString(arrays));
[I@1b6d3586
[1, 2, 3, 4, 5]
Process finished with exit code 0
-
public static void sort(T[] a, Comparator<? super T> c)
定制排序,后面会讲到
sort()
void sort(int[] a) : 无返回值,对arrays进行升序排列
其他基本数据类型(除了boolean),还有对象类型的重载方法都有
int[] arrays = {1, 10, 6, 14, 3};
System.out.println(arrays);
System.out.println(arrays.hashCode());
System.out.println(Arrays.toString(arrays));
Arrays.sort(arrays);
System.out.println(Arrays.toString(arrays));
[I@1b6d3586
460141958
[1, 10, 6, 14, 3]
[1, 3, 6, 10, 14]
Process finished with exit code 0
fill()
void fill(int[] a,int val) : 无返回值,将arrays数组中所有元素的值都改为value
int[] arrays = {1, 10, 6, 14, 3};
System.out.println(arrays);
System.out.println(Arrays.toString(arrays));
Arrays.fill(arrays, 2);
System.out.println(Arrays.toString(arrays));
[I@1b6d3586
[1, 10, 6, 14, 3]
[2, 2, 2, 2, 2]
Process finished with exit code 0
binarySearch()
int binarySearch(int[] a,int key):对排序后的数组进行二分法检索指定的值。找到,则返回找到的数据的下标,没找到,则返回 -(数组长度+1)
key为需要搜索的数字或对象等(含有多个构造方法)
// binarySearch()
@Test
public void test1(){
int[] a1 = new int[]{1,2,3,4,5};
System.out.println(Arrays.binarySearch(a1, 4));
System.out.println(Arrays.binarySearch(a1, 100));
}
3
-6
Process finished with exit code 0
copyOf()
JDK源码中最常见的方法,常见于容器类型中,比如集合中
-
public static T[] copyOf(T[] original, int newLength)
original为入参数组,newLength为入参数组大小
返回一个新创建的,实际类型为**Object[]**的数组,其大小和入参数组一样,其值和入参数组一样
-
还有类似的基本数据类型,重载方法,对应返回的数组实际类型为入参数组的对应的基本数据类型数组
基本数据类型是除了boolean的七大基本数值数据类型,byte,short,int,long,float,double,char
-
-
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
在两参数的基础上,提供第三个参数,用于使得新数组的实际类型为指定的入参类型
稀疏数组(自定义数组)
- 当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
- 稀疏数组的处理方式是:
- 记录数组一共有几行几列,有多少不同值(非0或者非x值)。
- 把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模。
稀疏数组一共三列数据,n行,对应n-1个不同数据。
- 第一(下标为0)行记录原始数组的总行数,总列数,和数组中存在的不同值个数(一般相同值不算在内)
- 其余行,记录不同值的下标,行下标,列下标,具体值