数组
- 数组是一种
引用数据类型
,存储方法和实例一样. - 数组隐式继承 Object。因此数组也可以调用 Object 类中的方法。
- 根据数组初始化方式不同分类:静态数组,动态数组。
- Java 数组存储元素的特点?
- 数组长度一旦确定不可变。
- 数组中元素数据类型一致,每个元素占用空间大小相同。
- 数组中每个元素在空间存储上,内存地址是连续的。
- 每个元素有索引,首元素索引 0,以 1 递增。
- 以首元素的内存地址作为数组对象在堆内存中的地址。
- 所有数组对象都有 length 属性用来获取数组元素个数。
1. 数组的创建
变量的初始化,实际上是三步完成的:声明 + 分配内存 + 赋值
-
声明(Declaration):这是定义变量类型的步骤,告诉编译器变量的名称和类型。
-
分配内存(Allocation):完成了变量的声明后,这一步由 JVM 自动进行
在这个阶段,对于基本数据变量,JVM 会为其在栈
中分配内存,但暂时还没有值绑定;
对于引用类型变量,JVM 会为这个变量 (实例) 的引用在栈
中分配空间,但是不会在堆
中分配空间来存储变量 (实例) 的实际内容。 -
赋值(Assignment):给变量一个具体的值,当变量被赋值后,它就被初始化了。
int a;
a = 10;
// 当然,也可以合并为一步
int a = 10;
1.1 声明数组
// 尽管两种方法完全相同,但第二种方法是沿袭 C/CPP 中的用法,应当使用第一种正宗 Java 语法
dataType[] arrayRefVar; // 首选的方法
//或
dataType arrayRefVar[]; // 效果相同,但不是首选方法
double[] myArraySample;
1.2 初始化数组
初始化就是给元素分配内存,//并为每个元素赋初始值//。
/**
* 1) 声明了一个数据类型为 dataType,引用名为 arrayRefVar 的数组
* 2) 使用 dataType[arraySize] 实例化一个数组。
* 3) 把新创建的数组的引用赋值给变量 arrayRefVar。
*/
dataType[] arrayRefVar = new dataType[arraySize];
1.3 静态/动态初始化
静态初始化
:由我们为每一个数组元素设置初始值, JVM 根据输入元素的个数,自动决定数组的长度;- 事前知道需要存储哪些数据,选用静态初始化。
dataType[] arrayRefVar = new dataType[]{元素1,元素2,…,元素n,};
int[] nums = new int[]{1,2,3};
// 省略 new 版
int[] nums = {1,2,3};
// 两步版
int[] nums;
nums = new int[]{1,2,3};
动态初始化
:由我们来设置数组的长度,而每一个数组元素的初始值由 JVM 来决定。可以看到,与静态初始化完全相反。- 事前不知道需要存储哪些数据,只能使用动态初始化。
dataType[] arrayRefVar = new dataType[ length ];
int[] nums = new int[5];
// 两步版
int[] nums;
nums = new int[5];
1.4 遍历数组
使用 for/ for-each/迭代器 皆可完成数组的遍历
1.5 扩容数组
数组长度一旦确定不可变。
为了扩容,只能用其他方法曲线救国
- 创建一个更大的数组将原数组中的数据全部拷贝到新数组中
- 使用 System.arraycopy() 方法完成数组的拷贝。
static int[] biggerArray(int[] oldArr) {
int[] newArr = new int[oldArr.length * 2];
// 第一个参数是原数组,第三个参数是新数组,第四个参数是复制元素的个数
// 第二个参数选择原数组的起始位置,第四个参数选择新数组的起始位置
System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
return newArr;
}
2. 二维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
// 可以看作总共有 2 个特殊的一维数组,每个特殊的一维数组内有 3 个元素
// 也可以看作 2 行 3 列的矩阵, 但是这种思想升到高维数组好就不好想象了.
int[][] a = new int[2][3];
3. Arrays 类的常用方法
- Arrays 类提供的都是静态方法,这意味着在需要时,可以直接通过 Arrays.方法名 调用,而不用先实例化一个 Arrays 对象
fill 为数组填充同一元素
- fill 只能为数组填充相同值的元素
Arrays.fill(int[] arr, int data)
// 在数组下标 ( fromIndex,toIndex ) 区间内填充同一元素
Arrays.fill(int[] a, int fromIndex, int toIndex, int val)
sort 数组排序
Arrays.sort(int[] arr)
Arrays.sort(int[] arr,int fromIndex, int toIndex)
Arrays.sort(String[] arr)
Arrays.sort(String[] arr, int fromIndex, int toIndex)
// 基于分治的归并排序算法,支持多核CPU排序,适合大数据量排序
Arrays.parallelSort(int[] arr)方法:
- 传入整形数组时,sort 方法底层采用快排实现
- sort 方法支持对字符串内的字符进行排序
- 对字符串内的字符进行排序时,默认区分大小写,此时大写字母在前
- 对字符串内的字符进行排序时,可以不区分大小写,而是严格按照字母表顺序排序
String[] strArray = new String[] { “z”, “a”, “C” };
Arrays.sort(strArray);
//输出: [C, a, z]
Arrays.sort(strArray, String.CASE_INSENSITIVE_ORDER);
//输出: [a, C, z]
toString 转字符串
- 对于 Object 原生的 toString 方法,其比较是对象的引用(对象的地址)是否相等
- Arrays 类重写了 toString方法,以字符串的形式输出数组中的每个元素
int[] arr = {3,2,1,5,4};
System.out.print(arr);//直接将数组打印输出
//输出:[I@7852e922 (数组的地址)
String str = Arrays.toString(arr); // Arrays类的toString()方法能将数组中的内容全部打印出来
//System.out.print(str);
//输出:[3, 2, 1, 5, 4]
equals 判断数组内元素是否相同
- 对于 Object 原生的 equals 方法,其比较是对象的引用(对象的地址)是否相等
- Arrays 类重写了 equals 方法,以判断两个数组内的元素是否完全相同
int[] arr1 = {1,2,3};
int[] arr2 = {1,2,3};
System.out.println(Arrays.equals(arr1,arr2));
System.out.println(arr1.equals(arr2));
//输出:true / false
//前者比较数组内的元素是否完全相等;后者比较的是两个数组对象的地址,两个不同的数组地址当然不同。
数组转 List asList
- 传入的数组必须是引用数据类型
/**从源码来看, asList 接收的是一个泛型变长参数
* 而 8 种基本类型是不能泛型化的,要想作为泛型参数就要使用其所对应的包装类。
*
* 再分析 listA 的 Size 为什么是 1 :
* listA 传递的是一个 int 类型的数组,int 是基本类型不能泛型化
* 但数组是一个引用类型,它是可以泛型化的,
* 也就是说例子中是把一个 int 类型的数组本身作为了 T 的类型,将数组自身,而非数组内的元素,存进了 list。
* 后边 ListA1 与 ListB 也就可以理解了,一个是进行了自动打包,一个是本来就是包装类型。
*/
int[] intArray = {1,2,3};
Integer[] integerArray = {1,2,3};
List listA = Arrays.asList(intArray);
List listA1 = Arrays.asList(1,2,3);
List listB = Arrays.asList(integerArray);
System.out.println(listA.size());//out:1
System.out.println(listA);// out:intArray 数组的地址[[I@7affc159]
System.out.println(listA1.size());//out:3
System.out.println(listB.size());//out:3
- 任何对数组/ List 实例的改动都会反过来影响到对方
// asList 返回的 List 实例,其底层与原数组共享相同的数据。
String[] str = new String[] { "apple", "avocado" };
List list = Arrays.asList(str);
str[0] = "pear";
System.out.println(list); //out : pear,avocado
- 【阿里开发手册】
asList 方法返回一个固定大小
的 List 对象,且该 List 对象不能使用修改 List 相关的方法。
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。 因此调用 add/remove/clear 方法会抛出 UnsupportedOperationException 异常
Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
/**Arrays 的 asList 方法使用的 ArrayList类是一个内部定义的类,而不是 java.util.ArrayList 类。
* 这个静态内部类中,将存储数组元素的变量设定为 final,
* 由此判断,这个静态内部类是不能做任何内部元素的添加删除操作的
* 其次,这个内部类里并没有 add,remove 方法,自然,调用 add 方法不可能成功
*/
String[] str = new String[] { "apple", "avocado" };
List list = Arrays.asList(str);
// 报运行时异常 UnsupportedOperationException 。
list.add("pear");
想要修改修改 asList 方法返回的 List 对象,可以尝试以下方法:
方法一:在 asList 方法外包一层 ArrayList ,将 Arrays.asList 方法返回的 List 对象转换为一个真正的 java.util.ArrayList 对象
String[] arr = new String[]{"apple", "avocado"};
List<String> list = new ArrayList<>(Arrays.asList(arr));
list.add("pear"); // 正常运行
System.out.println(list); //out : apple,avocado,pear
方法二:使用 Collections 类
我们也可以使用 Collections 类提供的静态方法,将 Arrays.asList() 方法返回的 List 对象转换为一个可修改的List对象
String[] arr = new String[]{"apple", "avocado"};
List<String> list = new ArrayList<>(Arrays.asList(arr));
Collections.addAll(list, "pear"); // 正常运行
System.out.println(list); //out : apple,avocado,pear
binarySearch 二分查找法找指定元素的下标
- 使用的前提是:数组必须
有序
copyOf 截取数组
int[] arr = {10,20,30,40,50};
int[] arr1 = Arrays.copyOf(arr, 3);
String str = Arrays.toString(arr1); // Arrays类的toString()方法能将数组中的内容全部打印出来
System.out.print(str);
//输出:[10, 20, 30] (截取arr数组的3个元素赋值给新数组arr1)
int []arr = {10,20,30,40,50};
int []arr1 = Arrays.copyOfRange(arr,1,3);
String str = Arrays.toString(arr1); // Arrays类的toString()方法能将数组中的内容全部打印出来
System.out.print(str);
//输出:[20, 30] (从第1位(0开始)截取到第3位(不包括))