一、数组的基本用法
1、什么是数组
数组本质上就是让我们能“批量”创建相同类型的变量
如果我们需要创建一个数据,int a;
需要创建两个数据,int a,int b;
需要创建三个数据,int a,int b,int c;
那如果要创建100万个数据,就不能创建一百万个变量,这时候就需要使用数组,帮我们批量创建。
注意:在java中,数组中包含的变量必须是相同类型
2、创建数组
//动态初始化
数据类型[] 数组名称 = new 数据类型[长度]{初始化数据};
int[] arr = new int[3]{1,2,3};
//静态初始化
数据类型[] 数组名称 = {初始化数据};
int[] arr = {1,2,3};
//静态初始化,数组元素个数和初始化数据的格式是一致的
3、数组的使用
int[] arr = {1,2,3};
//获取数组长度
System.out.println("length:"+arr.length);//3
System.out.println(arr[1]);//2
System.out.println(arr[0]);//1
arr[2] = 100;
System.out.println(arr[2]);//100
注意:
- 获取数组长度arr.length
- 数组的下标从0开始计数,下标的合法范围为[0,length-1],超出范围,会出现下标越界异常
- 数组不能被整体赋值
//遍历数组
int[] arr = {1,2,3};
for(int i = 0; i < arr.length;i++){
System.out.println(arr[i]);
}
//1
//2
//3
//使用for-each遍历
//for-each是for循环的另外一种使用方式,可避免循环条件和更新语句出错
int[] arr = {1,2,3};
for(int x : arr){
System.out.println(x);
}
//执行结果同上
二、数组作为方法的参数
1、基本用法
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
2、理解引用类型(重点/难点)
public static void main(String[] args){
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x){
x = 10;
System.out.println("x = " + x);
}
//执行结果 x = 10 num = 0
//我们可以看到,改变形参的值实参的值并没有改变,因为我们这个是值传递,
//形参只是一个临时变量,执行完func之后形参释放,自然也就没修改实参的值
//那么,要怎么通过调用函数改变实参的值
public static void main(String[] args){
int[] arr = {1,2,3};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a){
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
//执行结果 a[0] = 10 arr[0] = 10
//我们可以看到,此时改变形参的值,实参的值也随之改变
//此时数组名arr是一个引用,不再是简单的实参拷贝
什么是引用?
学过C语言,引用我们可以理解为指针
创建一个引用只是相当于创建了一个很小的变量,这个变量保存了一个整数,这个整数表示内存中的一个地址。
我们都知道,数组的内存空间是连续的,所以传arr相当于传了地址,数组的首地址,得到了数组的首地址,也就得到了整个数组,所以改变形参的值实参的值也随之改变,或者按图示理解,引用相当于一个线索,指向同一个内存空间,改变一个值就相当于改变了原有的数据的值,函数调用结束后,a的这个引用释放,但是值已经改变了。
3、认识null
null在java中表示一个“空引用”,也就是一个无效的引用
int[] arr = null;
System.out.println(arr[0]);
//执行结果
//Exception in thread "main" java.lang.NullPointerException
//at Test.main(Test.java:6)
虽然arr引用指向的是一个空引用,但是arr这个引用是存在的,不过由于他指向的是空引用,所以对他进行处理就会有空指针异常
三、数组作为方法的返回值
public class Test {
//数组作为方法的返回值
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
transform(arr);
printArray(arr);
}
private static void printArray(int[] arr) {
for(int i = 0;i <arr.length;i++){
System.out.println(arr[i]);
}
}
private static void transform(int[] arr) {
for(int i = 0;i < arr.length;i++){
arr[i]*=2;
}
}
}
//执行结果 2 4 6 8 10
//但是这个改变了原有数组,为了不破坏原有数组,提出以下改进方案
public class Test {
//数组作为方法的返回值
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int[] ret = transform(arr);
printArray(ret);
}
private static void printArray(int[] arr) {
for(int i = 0;i <arr.length;i++){
System.out.println(arr[i]);
}
}
private static int[] transform(int[] arr) {
for(int i = 0;i < arr.length;i++){
arr[i]*=2;
}
return arr;
}
}
//执行结果 2 4 6 8 10 创建了一个新的数组,将新数组返回,不破坏原有数组
//由于数组是引用类型,返回的时候只将新数组的地址传了回去,并没有拷贝新数组的内容,从而比较高效
四、有关数组的练习
1、数组转字符串
import java.util.Arrays;
int[] arr = {1,2,3,4,5,6};
String newArr = Arrays.toString(arr);
System.out.println(newArr);
//执行结果 [1,2,3,4,5,6]
java提供了java.util.Arrays包,其中包含了一些操作数的常用方法
什么是包?
例如做一碗饺子,要先准备饺子馅,饺子皮,包饺子,烧水,下锅煮熟,盛出来,蘸汁,但是为了方便,有时候我们会在超市买速冻饺子,即已经包好的饺子,再回去煮,这样省了很多步骤,降低了做饺子的难度。
同样,程序开发也不是所有的程序都是我们从最开始写的,这样当要做一个大工程时,效率大大降低,所以java中提供了大量的标准库(JDK提供的代码)和海量的第三方库(其他机构组织提供的代码)供我们使用,这些代码就放在一个一个的包中,所谓的包就相当于卖饺子的超市。
//自己实现的一个数组转字符串
public class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(toString(arr));
}
private static String toString(int[] arr) {
String ret = "[";
for(int i = 0;i < arr.length;i++){
ret +=arr[i];
if(i!=arr.length-1){
ret+=",";
}
}
ret+="]";
return ret;
}
}
2、数组拷贝
int[] arr = {1,2,3,4,5,6};
int[] newArr = Arrays.copyOf(arr,arr.length);
System.out.println("newArr:" + Arrays.toString(newArr));
arr[0] = 10;
System.out.println("arr:" + Arrays.toString(arr));
System.out.println("newArr:" + Arrays.toString(newArr));
//执行结果
newArr:[1, 2, 3, 4, 5, 6]
arr:[10, 2, 3, 4, 5, 6]
newArr:[1, 2, 3, 4, 5, 6]
//拷贝是重新申请了一片空间,当再改变arr数组中的值时newArr不变
//如下图所示
//自己实现的拷贝
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
int[] newArr = copyOfArr(arr);
System.out.println(Arrays.toString(newArr));
}
public static int[] copyOfArr(int[] arr){
int[] newArr = new int[arr.length];
for(int i = 0;i <newArr.length;i++){
newArr[i] = arr[i];
}
return newArr;
}
}
//运行结果 [1,2,3,4,5,6]
3、找数组中最大的元素
public class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(max(arr));//6
}
public static int max(int[] arr){
//先让第一个数是最大的
int max = arr[0];
for(int i = 1;i < arr.length;i++){
if(max<arr[i]){
max = arr[i];
}
}
return max;
}
4、求数组中元素的平均值
public class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6};
System.out.println(average(arr));
}
private static double average(int[] arr) {
int sum = 0;
for(int x : arr){
sum+=x;
}
return (double) sum/arr.length;
}
}
//注意,return的时候不能写成 return (double) (sum/arr.length);
//sum为整型,arr.length也是整型,除了之后也是整型,在转为double,有精度丢失
5、查找数组中指定元素
class Test{
public static void main(String[] args){
int[] arr = {1,2,3,4,5,6};
System.out.println(Find(arr,4));
}
public static int Find(int[] arr,int num){
for(int i = 0;i < arr.length;i++){
if(arr[i]==num){
return i;
}
}
return -1;//没有找到
}
}
//运行结果 3
//二分查找法(有序)
二分查找的思想:取中间位置的元素,如果相等返回,
如果比中间值大,在有半区间以相同的方式查找。
如果比中间值小,在左半区间以相同方法查找
class Test{
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 num){
int left = 0;
int right = arr.length - 1;
while(left<=right){
int mid = (left + right)/2;
if(a[mid]==num){
return mid;//第一次就找到了
}else if(a[mid] < num){
left = mid +1;//右侧区间查找
}else{
right = mid - 1;//左侧区间查找
}
}
return -1;//没找到
}
}
6、检查数组的有序性
//给定一个整型数组,判断该数组是否有序
class Test{
public static void main(String[] args){
int[] arr = {1,2,3,10,5,6};
System.out.println(isSorted(arr));
}
public static boolean isSorted(int[] arr){
for(int i = 0;i < arr.length - 1;i++){
if(arr[i] > arr[i+1]){
return false;
}
}
return true;
}
}
7、冒泡排序
//思想:每次找到待排区间最大(小)的数,放到数组的最后面(前面)
//性能较低
import java.util.Arrays;
class Test{
public static void main(String[] args){
int[] arr = {9,5,2,7};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr){
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr.length-i;j++){
if(arr[j] > arr[j+1]){
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
}
//运行结果 [2,5,7,9]
8、数组逆序
有多种方法,现在看一下比较高效的
import java.util.Arrays;
class Test{
public static void main(String[] args){
int[] arr = {1,2,3,4};
reverse(arr);
System.out.println(Arrays.toString(arr));
}
public static void reverse(int[] arr){
int left = 0;
int right = arr.length - 1;
while(left<right){
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
}
9、数组数字排列
//给定一个数组,将所有偶数放到前面,奇数放到后面
//从数组的两头走,把前面遇到的奇数和后面遇到的偶数交换
public class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6};
transform(arr);
System.out.println(Arrays.toString(arr));
}
public static void transform(int[] arr){
int left = 0;
int right = arr.length - 1;
while(left < right){
while(left<right&&arr[left]%2==0){
left++;
}
while(left<right&&arr[right]%2!=0){
right--;
}
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
//运行结果 [6, 2, 4, 3, 5, 1]