赞
踩
一维数组是线性结构。二维数组,三维数组,多维数组是非线性结构。
一维数组的声明格式有以下两种:
数组元素的类型[] 变量名称
(变量名称即引用,保存的是数组的地址)数组元素的类型 变量名称[]
数组元素的类型,可以是 java 中的任意类型,变量名称可以是任意合法的标识符,上面两种
格式较常用的是第一种,例如:
int [] a;
Student[] stu;
在一行中也可以声明多个数组,例如:int[] a, b, c
数组有两种创建方式
使用 new 操作符来创建数组,格式为:new 数组元素的数据类型[数组元素的个数]
,动态初始化数组时,会给数组中的元素赋该类型的默认值!!!
package arraytest; public class ArrayTest01 { public static void main(String[] args) { //声明 int 类型的数组,长度为 5 //数组中的元素必须为 int 类型 int[] data = new int[5]; //对数组中的元素进行赋值,如果不赋值默认为该类型的默认值,以上数组默认为 0 //如何赋值?变量名[下标],下标从 0 开始 data[0] = 1; data[1] = 2; data[2] = 3; data[3] = 4; data[4] = 5; //输出数组中的元素,变量名[下标] System.out.println(data[0]); System.out.println(data[1]); System.out.println(data[2]); System.out.println(data[3]); System.out.println(data[4]); System.out.println("-----------------------"); //采用 length 属性可以取得数组的长度 for (int i=0; i<data.length; i++) { System.out.println(data[i]); } //输出指定的数组元素 System.out.println("data[3]=" + data[3]); //数组下标越界 //会抛出 ArrayIndexOutOfBoundsException 异常 System.out.println("data[10]=" + data[10]); //不能成功赋值,数组中的类型必须是一种类型 //data[0] = "iiii"; } }
内存结构变化:
必须清楚数组为引用类型,它在堆中分配。
package arraytest; public class ArrayTest02 { public static void main(String[] args) { //声明引用类型的数组 Student[] students = new Student[2]; students[0].id = 1; students[0].name = "Susan"; students[1].id = 2; students[1].name = "Jack"; } }
public class Student{
int id;
String name;
}
出现空指针异常:
对于引用类型的数组,一定要注意,new完数组并没有new完其中的对象,其中的对象在内存中还是默认值null,所以一定要再把对象new出来!
package arraytest; public class ArrayTest03 { public static void main(String[] args) { //声明引用类型的数组 Student[] student = new Student[2]; // //方式一: // student[0] = new Student(); // student[0].id = 1; // student[0].name = "Susan"; // // student[1] = new Student(); // student[1].id = 2; // student[1].name = "Jack"; //方式二: Student stu1 = new Student(); stu1.id = 1; stu1.name = "Susan"; student[0] = stu1; Student stu2 = new Student(); stu2.id = 2; stu2.name = "Jack"; student[1] = stu2; for (int i=0; i<student.length; i++) { System.out.println("id=" + student[i].id + ", name=" + student[i].name); } } }
运行结果:
内存结构变化图:
使用数组的初始化语句,格式有两种:
数组元素的类型[] 变量名称 = {数组元素1, 数组元素2,...,数组元素n}
或数组元素的类型 变量名称[] = {数组元素1, 数组元素2, ...,数组元素n}
;数组元素的类型[] 变量名称 =new 数组元素的类型[]{数组元素1, 数组元素2,...,数组元素n}
。package arraytest; public class ArrayTest04 { public static void main(String[] args) { //静态初始化 int[] data = {1,2,3,4,5}; for (int i = 0; i < data.length; i++) { System.out.println(data[i]); } Student stu1 = new Student(); stu1.id = 1; stu1.name = "Susan"; Student stu2 = new Student(); stu2.id = 2; stu2.name = "Jack"; //静态初始化 Student[] students = {stu1, stu2}; for (int i=0; i<students.length; i++) { System.out.println("id=" + students[i].id + ", name=" + students[i].name); } } }
运行结果:
一个错误写法:
package arraytest; public class ArrayTest05 { public static void main(String[] args) { int[] a = {1,2,3}; printArray(a); System.out.println("==============="); //没有这种语法 // printArray({1,2,3}); //直接传递一个静态数组的话,语法必须这样写 printArray(new int[]{1,2,3}); } private static void printArray(int[] a) { for (int i = 0; i < a.length; i++) { System.out.println(a[i]); } } }
编译报错:
注释后再运行:
String[] strs= new String[0]
或String strs={}
//均表示数组对象创建了,但是数组中没有任何数据。
对于main方法中所传的String[] args
数组,主要是用来接收用户输入参数的,长度默认为0,在运行时,若使用:java 类名 abc sde
就是相当于传了两个元素进该数组。(了解即可)
当创建数组时,确定数组中所需存储的元素,采用静态初始化;
否则采用动态初始化,预先分配内存。
数组的长度一旦确定不可变,若需扩容需要先新创建一个大容量的数组,然后将后将小容量数组中的数据一个个拷贝到大数组中去。
结论: 数组扩容由于涉及拷贝问题,所以其扩容效率较低,应尽量避免进行数组扩容,在创建时预估好其长度。
数组拷贝函数:
System.arraycopy(源数组名称,拷贝起始位置,目标数组名称,存放起始位置,拷贝长度);
对于引用数据类型,其拷贝的其实也就是对象的地址。
二维数组属于多维数组。多维数组即数组元素的类型也是数组,可以这么理解:二维数组其实就是一个特殊的一维数组,该一维数组中每一个元素都是一个一维数组「每个一维数组长度不定」。
而三维数组就是一个特殊的二维数组,其中每一个元素都是一个一维数组。[三维数组几乎不用]
格式如下:
int [][] data;
package arraytest; public class ArrayTest06 { public static void main(String[] args) { // 声明二维数组 int[][] data = new int[2][3]; //两行三列,两个一维数组,每个一维数组中3个元素 // 对二维数组赋值 data[0][0] = 1; data[0][1] = 2; data[0][2] = 3; data[1][0] = 4; data[1][1] = 5; data[1][2] = 6; //输出二维数组 //data.length是其所含一维数组的个数 //data[i].length是对应一维数组i中元素的个数 for (int i = 0; i < data.length; i++) { for (int j = 0; j < data[i].length; j++) { System.out.println(data[i][j]); } } } }
运行结果:
内存结构变化图:
package arraytest; public class ArrayTest07 { public static void main(String[] args) { //从高维开始逐维创建 int[][] data = new int[2][]; data[0] = new int[2]; data[1] = new int[4]; data[0][0] = 1; data[0][1] = 2; data[1][0] = 1; data[1][1] = 2; data[1][2] = 3; data[1][3] = 4; //输出二维数组 for (int i=0; i<data.length; i++) { for (int j=0; j<data[i].length; j++) { System.out.println(data[i][j]); } } } }
运行结果:
package arraytest;
public class ArrayTest08 {
public static void main(String[] args) {
//静态初始化,多个数组之间用逗号隔开
int[][] data = {{1,2},{3,4,5}}; //也可以使用int[][] data =new int[][]{{1,2},{3,4,5}};
for (int i=0; i<data.length; i++) {
for (int j=0; j<data[i].length; j++) {
System.out.println(data[i][j]);
}
}
}
}
假设有 5 个数字 3,1,6,2,5 在一个 int 数组中,要求按从小到大排序输出,如何采用冒泡排序算法呢?
冒泡排序的算法是这样的,首先从数组的最左边开始,取出第 0 号位置(左边)的数据和第1号位置(右边)的数据,如果左边的数据大于右边的数据,则进行交换,否而不进行交换。接下来右移一个位置,取出第 1 个位置的数据和第 2 个位置的数据,进行比较,如果左边的数据大于右边的数据,则进行交换,否而不进行交换。沿着这个算法一直排序下去,最大的数就会冒出水面,这就是冒泡排序。
以上示例排序过程如下:
从上面我们看到了比较了 N-1 次,那么第二遍就为 N-2 次比较了,如此类推,比较次数的公式如下:(N-1) + (N-2)+…+1=((N-1)*N)/2,所以以上总共比较次数为((5-1)*5)/2=10。时间复杂度为:
O
(
n
2
)
O(n^2)
O(n2)。
具体代码如下:
package arraytest; public class ArraySortTest01 { public static void main(String[] args) { int[] data = {3,1,6,2,5}; // data.length - 1是因为最后一轮不需要排序 for (int i = 0; i < data.length - 1; i++) { // data.length -1 - i 是因为每一轮都能确定排序好一个数(最大的数最先确定其顺序) for (int j = 0; j < data.length -1 - i; j++) { if(data[j]>data[j+1]){ int temp = data[j]; data[j] = data[j+1]; data[j+1] = temp; } } } for (int j = 0; j < data.length; j++) { System.out.print(data[j] + " "); } } }
运行结果:
因为冒泡排序存在提前排序成功的可能,因此可以对以上冒泡排序算法进行优化,此处的优化思路使用了“假设法”来实现,若当前一轮的比较不存在任何元素的交换,则说明已有序,跳出排序循环。
如下代码:
package arraytest; public class ArraySortTest01 { public static void main(String[] args) { int[] data = {3,1,6,2,5}; // data.length - 1是因为最后一轮不需要排序 for (int i = 0; i < data.length - 1; i++) { boolean flag = true; // data.length -1 - i 是因为每一轮都能确定排序好一个数(最大的数最先确定其顺序) for (int j = 0; j < data.length -1 - i; j++) { if(data[j]>data[j+1]){ int temp = data[j]; data[j] = data[j+1]; data[j+1] = temp; //当前比较存在交换,flag置为false flag = false; } } //当前一整轮比较都不存在交换,说明数组已经有序,提前退出 if(false){ break; } } for (int j = 0; j < data.length; j++) { System.out.print(data[j] + " "); } } }
选择排序对冒泡排序进行了改进,使交换次数减少(交换更有意义),但比较次数仍然没有减少。
假设有 5 个数字 3,1,6,2,5 在一个 int 数组中,要求按从小到大排序输出,采用选择排序,选择排序是这样的,先从左端开始,找到下标为 0 的元素,然后和后面的元素依次比较,如果找到了比下标 0 小的元素,那么再使用此元素,再接着依次比较,直到比较完成所有的元素,最后把最小的和第 0 个位置交换,就为最小的数找到了位置,第一遍排序结束。接着第二遍开始从下标1的元素和户名的元素开始比较,以此类推,直到数组有序。
以上示例排序过程如下:
第二遍排序将从下标为 1 的元素开始,以此类推,经过 N(N-1)/2 次比较(时间复杂度仍为
O
(
n
2
)
O(n^2)
O(n2)),经过 N 次数据交互就完成了所有元素的排序。
具体代码如下:
package arraytest; public class ArraySortTest02 { public static void main(String[] args) { int[] data = {3,1,6,2,5}; // data.length - 1是因为最后一轮不需要排序 for (int i = 0; i < data.length - 1; i++) { // min = i 是因为每一轮都能确定排序好一个数(最小的数最先确定其顺序) int min = i; for (int j = i+1; j < data.length ; j++) { //保存当前比较的小数的下标 if(data[min]>data[j]){ min = j; } } //进行位置交换 if(min != i){ //先判断一下,保证交换有效 int temp = data[min]; data[min] = data[i]; data[i] = temp; } } for (int i = 0; i < data.length; i++) { System.out.print(data[i] + " "); } } }
运行结果:
采用逐一比对的方式进行数组的遍历,如果发现了匹配值,返回数组下标即可,这种方式称为线性查找,是一种最简单粗暴的查找法。
优点: 查找数组无需有序;
缺点: 查找的次数多,效率低下,【比较适合小型数组,大型数组效率太低】。
如果一个数组已经排好序,那么我们可以采用效率比较高的二分查找(折半查找)算法。
以上就是二分或折半查找法,注意:二分或折半查找法必须保证数组事先是排好序的。
假设查找的数组为升序排序,则首先定义两个变量,分别用于保存查找元素(value)所在范围的最小索引值(min)和最大索引值(max)。
然后开启二分查找,每次查找前都定义一个mid变量,并设置该变量的初始值为:(max + min)/2。在查找的过程中,发生以下三种情况,则做对应的处理。
1)如果arr[mid]大于value,则证明查找的元素在mid的左侧,那么更新max的值为:mid-1;
2)如果arr[mid]小于value,则证明查找的元素在mid的右侧,那么更新min的值为:mid+1;
3)如果arr[mid]等于value,则证明查找元素的索引值就是mid,返回mid的值即可!
在以上的操作中,我们不停的更改min和max的值,如果发生min大于max的情况,则证明查找的元素不存在,那么返回-1(表示找不到)即可!
package arraytest; public class BinarySearchTest01 { public static void main(String[] args) { int[] data = {11,12,13,14,15,16,17,18,19,20}; int index = binarySearch(data, 18); System.out.println("18的下标为:" + index); } public static int binarySearch(int[] data, int value){ // 开始下标 int low = 0; // 结束下标 int high = data.length - 1; while (low <= high) { //循环条件:开始元素的下标<=结束元素的下标 int mid = (low + high) >> 1; // 相当于(low + high)/2 , 写法要注意,不是(low + high) >> 2!!! if (data[mid] > value) { //目标值在数组前半部分 high = mid - 1; } else if (data[mid] < value) { //目标值在数组后半部分 low = mid + 1; } else if (data[mid] == value) { return mid; //找到目标下标,直接返回 } } //没有找到对应元素,返回-1 return -1; } }
再找下11的下标,结果:
实际开发中,不用自己写排序、查找等算法,直接用工具类即可
基于快速排序算法,适合小规模数据量排序。
package arraytest; import java.util.Arrays; //已写好的工具包 public class ArraysUtilTest01 { public static void main(String[] args) { int[] data = {3,1,6,2,5}; Arrays.sort(data); //排序方法,默认升序排序 for (int i = 0; i < data.length ; i++) { System.out.print(data[i] + " "); } System.out.println("\n----------------------------"); for (int i = data.length - 1 ; i >= 0; i--) { System.out.print(data[i] + " "); } System.out.println("\n----------------------------"); } }
运行结果:
如下代码:
package arraytest; import java.util.Arrays; public class ArraysUtilTest02 { //对自定义的引用类型的数组进行排序,不能直接调用Arrays.sort public static void main(String[] args) { Student student1 = new Student(1,"wang"); Student student2 = new Student(2,"liu"); Student student3 = new Student(3,"zhang"); Student student4 = new Student(4,"xie"); Student[] students = new Student[]{student1,student2,student3,student4}; Arrays.sort(students); System.out.println(Arrays.toString(students)); //需要重写Student的toString方法 } }
package arraytest;
public class Student {
int id;
String name;
public Student() {
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
报错(出现类型转换异常):
因为此时Student
类还不是可比较的,需要继承Comparable
类,重写其中的compareTo()
方法,如下:
package arraytest; public class Student implements Comparable{ int id; String name; public Student() { } public Student(int id, String name) { this.id = id; this.name = name; } //compareTo方法中写自定义的比较规则,前大后小返回1,前小后大返回-1,相等返回0, // 定义完后Student就是可比较的了,具体比较时不需要手动调用本方法,由JDK自动调用。 @Override public int compareTo(Object o) { Student s = (Student) o; // return this.name.compareTo(s.name); //按名字进行比较,即字典顺序,调用String类覆写的compareTo方法 return this.id-s.id; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
按id
进行比较:
按name
进行比较:
由上1.6.1.2中按name
进行排序可以看出,String
类已有覆写compareTo()方法,所以其也是可比较的,可以直接调用Arrays.sort()
方法对其按字典顺序排序。
package arraytest;
import java.util.Arrays;
public class ArraysUtilTest03 {
public static void main(String[] args) {
String[] strings = {"a","d","b","c"};
Arrays.sort(strings);
System.out.println(Arrays.toString(strings));
}
}
运行结果:
Arrays.parallelSort(int[] arr)方法:Java 8引入的方法,基于分治的归并排序算法,支持多核CPU排序(需要确保所用电脑是支持多核的),适合大数据量排序,效率较高。如果数据量太小的话,不要调用这个方法,因为启动多核也是需要耗费资源的。
如下代码(我的电脑好像不支持多核,结果就不放了):
package arraytest; import java.util.Arrays; import java.util.Random; public class ArraysUtilTest04 { public static void main(String[] args) { //创建数组模拟大数据 int[] arr = new int[100000000]; Random random = new Random(); for (int i = 0; i < arr.length; i++) { arr[i] = random.nextInt(100000000); } //System.currentTimeMillis()获取当前时间毫秒数(1970-1-1 0:0:0 000到当前系统时间的总毫秒数,1秒=1000毫秒) long begin = System.currentTimeMillis(); //排序 Arrays.parallelSort(arr); long end = System.currentTimeMillis(); //统计排序耗时 System.out.println(end-begin); long begin1 = System.currentTimeMillis(); Arrays.sort(arr); long end1 = System.currentTimeMillis(); System.out.println(end1-begin1); } }
注意:只能对有序数组使用。
package arraytest; import java.util.Arrays; public class ArraysUtilTest05 { public static void main(String[] args) { int[] data = {3,1,6,2,5}; Arrays.sort(data); //先排序,有序后再二分查找! System.out.println("排序后:"); for (int i = 0; i < data.length ; i++) { System.out.print(data[i] + " "); } System.out.println(); int index = Arrays.binarySearch(data,2); //二分查找算法,有目标元素的话,会返回元素下标,否则返回-1 System.out.println("2的下标为:" + index); int index2 = Arrays.binarySearch(data, 0); System.out.println("0的下标为:" + index2); } }
运行结果:
Arrays.toString()方法:将一维数组转换成字符串
Arrays.deepToString()方法:将多维数组转换成字符串
package arraytest; import java.util.Arrays; public class ArraysUtilTest06 { public static void main(String[] args) { toStringTest(); deepToStringTest(); } /** * Arrays.toString():将一维数组转换为字符串 */ public static void toStringTest(){ int[] arr = {1,2,3,4,5}; System.out.println(arr); //[I@1b6d3586 System.out.println(Arrays.toString(arr)); //[1, 2, 3, 4, 5] String[] name = {"lili","Mary","Lucy"}; System.out.println(name);//[Ljava.lang.String;@4554617c System.out.println(Arrays.toString(name)); //[lili, Mary, Lucy] } /** * Arrays.deepToString():将多维数组转换为字符串 */ public static void deepToStringTest(){ int[][] arr2 ={{6,7,8},{9,10,11}}; System.out.println(arr2);//[[I@74a14482 System.out.println(Arrays.toString(arr2)); //[[I@1540e19d, [I@677327b6], 只能解析一维 System.out.println(Arrays.deepToString(arr2));//[[6, 7, 8], [9, 10, 11]] } }
与Arrays.sort()
方法类似,如果要对自定义引用数据类型的数组使用Arrays.toString()
方法,可以对其覆写toString()
方法,否则输出的是一堆“地址”。
Arrays.equals(int[] arr1, int[] arr2)方法:判断两个数组是否相等
Arrays.deepEquals(Object[] arr1, Object[] arr2)方法:判断两个多维数组是否相等
package arraytest; import java.util.Arrays; public class ArraysUtilTest07 { public static void main(String[] args) { equalsTest(); deepEqualsTest(); } /** * Arrays.equals():判断两个一维数组内容是否相等 */ public static void equalsTest(){ int[] arr1 = {1,2,3}; int[] arr2 = {1,2,3}; System.out.println(Arrays.equals(arr1, arr2));//true String[] name1 = new String[]{"lili","Mary","Lucy"}; String[] name2 = new String[]{"lili","Mary","Lucy"}; System.out.println(Arrays.equals(name1, name2));//true } /** * Arrays.deepEquals():判断两个多维数组内容是否相等 */ public static void deepEqualsTest(){ int[][] arr1 = {{1,2,3},{4,5,6}}; int[][] arr2 = {{1,2,3},{4,5,6}}; System.out.println(Arrays.deepEquals(arr1, arr2));//true } }
也可以直接调用Arrays.equals()
方法:
package arraytest;
import java.util.Arrays;
public class ArraysUtilTest08 {
public static void main(String[] args) {
Person person1 = new Person(1,"wang");
Person person2 = new Person(2,"liu");
Person person3 = new Person(3,"zhang");
Person person4 = new Person(4,"xie");
Person[] persons1 = new Person[]{person1,person2,person3,person4};
Person[] persons2 = new Person[]{person1,person2,person3,person4};
System.out.println(Arrays.equals(persons1,persons2)); //true
}
}
package arraytest;
public class Person {
int age;
String name;
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
Arrays.fill(int[] arr, int data)方法:填充数组
Arrays.fill(int[] a, int fromIndex, int toIndex, int val)方法
如下代码:
package arraytest;
import java.util.Arrays;
public class ArraysUtilTest09 {
public static void main(String[] args) {
int[] arr = new int[5]; //5个0
Arrays.fill(arr, 10); //对数组填充10
System.out.println(Arrays.toString(arr));//[10, 10, 10, 10, 10]
Arrays.fill(arr,1, 3, 5); //对数组指定范围(下标1-2,不包括3)填充5
System.out.println(Arrays.toString(arr));//[10, 5, 5, 10, 10]
}
}
int[] Arrays.copyOf(int[] original, int newLength)方法:数组拷贝
int[] Arrays.copyOfRange(int[] original, int from, int to)
package arraytest; import java.util.Arrays; public class ArraysUtilTest10 { public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8}; //数组拷贝,拷贝3个长度 int[] newArr = Arrays.copyOf(arr, 3); System.out.println(Arrays.toString(newArr));//[1, 2, 3] //数组拷贝,拷贝从下标3-5的数据,不包括下标6 int[] newArr2 = Arrays.copyOfRange(arr, 3, 6); System.out.println(Arrays.toString(newArr2));//[4, 5, 6] } }
Arrays.asList(T… data)方法:将一组数据转换成List集合(T… data是一个可变长度参数)
package arraytest; import java.util.Arrays; import java.util.List; public class ArraysUtilTest11 { public static void main(String[] args) { //将一串数据转换为List集合 List<Integer> list = Arrays.asList(1, 34, 54, 56, 76, 4, 67, 2); for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i) + " "); } System.out.println(); } }
注意:对于数组中最后一个元素的增删,是不会有效率影响的。
在Java中,一旦你初始化了一个数组,就不能再用这种方式再次为同一个数组重新初始化。也就是说,一旦你声明并初始化了一个数组,它的大小和内容就固定了,不能再用另一个数组的内容或大小再次初始化它。
如果你想改变数组的内容,可以通过如下方法:
int[] d = {1,2,3};
for (int i=0; i<d.length; i++) {
d[i] += 3;
}
int[] d = {1,2,3};
int[] d2 = {4,5,6};
d = d2;//现在d指向了新的数组{4, 5, 6},原来的数组会被垃圾回收
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。