一.数组基本用法
1.何为数组?
所谓数组,就是相同类型元素的集合。数组的本质无疑就是让我们能 "批量" 创建和操作相同类型的变量。
例如:
如果需要表示两个数据, 那么直接创建两个变量即可 int a; int b
如果需要表示五个数据, 那么可以创建五个变量 int a1; int a2; int a3; int a4; int a5;
但是如果需要表示一万个数据, 那么就不能创建一万个变量了. 这时候就需要使用数组, 帮我们批量创建。
注意事项: 在 Java 中, 数组中包含的变量必须是 相同类型
2.创建数组
基本语法:
1>动态初始化:数据类型[] 数组名称 = new 数据类型 [ ] { 初始化数据 };
int[] arr1 = new int[]{1, 2, 3};
int[] arr2 = new int[4];
【注意】
-
数组名字,即上面的arr1、arr2都是一个引用,用来存放对象的地址;这里的对象就是new产生的。
-
定义数组时,带初始化一定不能制定数组长度。例如arr1数组后面初始化了三个元素,长度默认是3,不需要指定。
-
定义数组时,没有初始化的数组一定要指定长度。例如arr2数组后面没有初始化,此时就必须指定数组长度,否则在开辟空间时,并不知道数组的大小,会报如下错误:
Error:(16, 29) java: 缺少数组维
-
arr2数组初始化只能通过for循环来初始化
-
数组定义后没有初始化默认值是0,如果数组里的元素是引用类型则默认值是null。
2>静态初始化:数据类型[ ] 数组名称 = { 初始化数据 };
int[] arr = {1, 2, 3};
3.数组的长度
1>数组的长度一般通过两种方式来指定:
2>在定义时一旦指定了长度,后面是不能修改的
int[] arr4={};
arr4[0]={4};//错误,企图将arr4的长度改为1
3>访问数组的长度:
使用length属性来访问,即 数组名.length;
int[] arr5={1,2,3};
//通过arr5.length访问数组长度
System.out.print(arr5.length);//3
【注意】
- 区分字符串中的length和数组中的length,数组中的length是一个属性,而字符串中的length是一个方法,使用时用一定的区别。
//数组长度(属性):arr5.length
int[] arr5={1,2,3};
System.out.print(arr5.length);
//字符串长度(方法):str.length()
String str="qwer";
System.out.print(str.length());
- ·在初始化数组或者字符串为NULL时,会报空指针异常的错误
代码
int[] arr6=null;
System.out.print(arr6.length);
String str=null;
System.out.print(str.length());
错误
Exception in thread "main" java.lang.NullPointerException
所以在初始化数组和字符串的时候一定要注意正确赋值及定义长度!!
4、访问数组元素
1>使用 [ ] 按下标取数组元素. 需要注意, 下标从 0 开始计数,[ ] 操作既能读取数据, 也能修改数据
int[] arr = {1, 2, 3};
// 访问数组中的元素
System.out.println(arr[0]); // 执行结果: 1
System.out.println(arr[1]); // 执行结果: 2
//修改最后一个元素的数据
arr[2] = 100;
System.out.println(arr[2]); // 执行结果: 100
2>下标访问操作不能超出有效范围 [0, length - 1] , 如果超出有效范围, 会出现下标越界异常
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
// 执行结果
Exception in thread"main"java.lang.ArrayIndexOutOfBoundsException: 100 at Test.main(Test.java:4)
抛出了 java.lang.ArrayIndexOutOfBoundsException 异常. 使用数组一定要下标谨防越界.
3> 遍历数组
所谓 "遍历" 是指将数组中的所有元素都访问一遍, 不重不漏. 通常需要搭配循环语句.
for循环遍历临时数组:
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 执行结果
1
2
3
for-each 遍历数组
int[] arr = {1, 2, 3};
for (int x : arr) {
System.out.println(x);
}
// 执行结果
1
2
3
- x变量的类型时数组的类型,代表数组中的每一个元素,冒号后面是数组名
- for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.
- for-each 和 for的区别:for循环时通过下标来访问数组,而 for-each只能通过默认的方式从左往右依次遍历,不能够通过下标任意访问某个元素。
toString()方法遍历打印数组
int[] arr = {1, 2, 3};
//调用方法打印数组
System.out.print(Arrays.toString(arr5));
//执行结果
[1, 2, 3]
在使用此方法时需要导入import java.util.Arrays包,在idea软件中,如果没有导入包,可以将鼠标放在Arrays上点击ALt并回车即可引入工具包。
二,数组作为参数使用
基本用法:打印数组内容
public static void main(String[] args) {
int[] arr = {1, 2, 3};
printArray(arr);
}
public static void printArray(int[] a) {
for (int x : a) {
System.out.println(x);
}
}
// 执行结果
1
2
3
- int[] a 是函数的形参, int[] arr 是函数实参.
- 如果需要获取到数组长度, 同样可以使用 a.length
三、数组作为方法的返回值
代码示例: 写一个方法, 将数组中的每个元素都 *2
第一种:直接修改原数组
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
transform(arr);
printArray(arr);
}
//打印数组的元素
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
// 直接修改原数组的方法
public static void transform(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
}
}
}
//执行结果
2
4
6
这个代码固然可行,直接将原始数组地址传过来,对原始数组进行修改。
但是破坏了原有数组. 有时候我们不希望破坏原数组, 就需要在方法内部创建一个新的数组, 并由方法返回出来。
第二种:直接修改原数组
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
int[] output = transform(arr);
printArray(output);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
// 返回一个新的数组
public static int[] transform(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i] * 2;
}
return ret;
}
}
在函数内部新定义了一个数组,并且将他的值初始化为原始数组的2倍。相当于是新开辟了一段空间,两个数组各自有各自的空间,这样的话就不会破坏原有数组了.
另外由于数组是引用类型, 返回的时候只是将这个数组的首地址返回给函数调用者, 没有拷贝数组内容, 从而比较高效.
四、数组的拷贝
数组拷贝有四种方式:
1.System.arraycopy(原始数组,从哪拷,目的数组,拷向哪,拷贝的长度);
int[] arr5 = {1, 2, 3};
System.out.println(Arrays.toString(arr5));
int[] arr6=new int[3];
//调用拷贝函数
System.arraycopy(arr5,0,arr6,0,arr5.length);
System.out.print(Arrays.toString(arr6));
//执行结果
[1, 2, 3]
[1, 2, 3]
上面代码是将数组arr5 的数据拷贝到数组 arr6中,并且拷贝arr5的长度。
2.Arrays.copyOf(原始数组,原始数组长度);
int[] arr5 = {1, 2, 3};
System.out.println(Arrays.toString(arr5));
//创建并以拷贝的方式初始化数组
int[] arr6=Arrays.copyOf(arr5,arr5.length);
System.out.print(Arrays.toString(arr6));
//执行结果
[1, 2, 3]
[1, 2, 3]
3. clone()方法;
int[] arr5 = {1, 2, 3};
System.out.println(Arrays.toString(arr5));
//创建并以拷贝的方式初始化数组
int[] arr6=arr5.clone();
System.out.print(Arrays.toString(arr6));
//执行结果
[1, 2, 3]
[1, 2, 3]
4. for循环遍历拷贝;
int[] arr5 = {1, 2, 3};
System.out.println(Arrays.toString(arr5));
int[] arr6=new int[arr5.length];
//通过for循环遍历拷贝
for(int k=0;k<arr5.length;k++)
{
arr6[k]=arr5[k];
}
System.out.print(Arrays.toString(arr6));
//执行结果
[1, 2, 3]
[1, 2, 3]
【注意】
深拷贝:当数组当中为简单类型的时候,上述前三种拷贝方法都为深拷贝,拷贝完修改值不会影响数组当中的值。
浅拷贝:当数组中的元素为引用类型的时候,只拷贝地址,上述前三种方法叫做浅拷贝,浅拷贝修改数据之后会影响原对象的值。浅拷贝要想拷贝完后修改值仍然不影响原数组的值,则需要将数组中的对象再拷贝一份放在目的数组中。
当数组中元素为引用类型时,浅拷贝修改数据之后会影响原对象的值。
浅拷贝要想拷贝完后修改值仍然不影响原数组的值,则需要将数组中的对象再拷贝一份放在目的数组中,即由浅拷贝达到深拷贝。见下图:
五、查找数组中指定元素(二分查找)
针对有序数组, 可以使用更高效的二分查找.
啥叫有序数组?
有序分为 "升序" 和 "降序" 如 1 2 3 4 , 依次递增即为升序. 如 4 3 2 1 , 依次递减即为降序.
以升序数组为例, 二分查找的思路是先取中间位置的元素, 看要找的值比中间元素大还是小. 如果小, 就去左边找; 否则 就去右边找.
1.自定义函数实现二分查找
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(binarySearch(arr, 6));
}
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (toFind > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
return mid;
}
}
// 循环结束, 说明没找到
return -1;
}
// 执行结果
5
除了自己写二分查找的代码,还可以使用系统内的二分查找的方法:binarySearch(数组名, 要查找的数);
2.binarySearch(数组名, 要查找的数)方法
class Test {
static int count = 0; // 创建一个成员变量, 记录二分查找循环次数
public static void main(String[] args) {
int[] arr = makeBigArray();
int ret = binarySearch(arr, 9999);
System.out.println("ret = " + ret + " count = " + count);
}
public static int[] makeBigArray() {
int[] arr = new int[10000];
for (int i = 0; i < 10000; i++) {
arr[i] = i;
}
return arr;
}
3.递归实现二分查找
public static int halfLocate(int[] arr,int left,int right,int key){
int mid=(left+right)/2;
if(key==arr[mid])
{
return mid;
}else if(arr[mid]>key)
{
return halfLocate(arr,left,mid-1,key);
}else{
return halfLocate(arr,mid+1,right,key);
}
}