IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【Java】剖析数组的使用 -> 正文阅读

[Java知识库]【Java】剖析数组的使用


数组(Array)是 相同类型的变量的集合,若将该集合命名,那么这个名称为 数组名。组成数组的各个变量称为数组的元素。

(本文画的图里的地址均为杜撰,了解其含义即可)

一、创建数组

??基本语法形式:

// 静态初始化
数据类型[] 数组名称 = { 初始化数据 };
// 动态初始化
数据类型[] 数组名称 = new int[元素个数];
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5,6}; //方法一
        int[] array2 = new int[10];   //方法二 初始化的数据默认为0
        int[] array3 = new int[]{1,2,3,4,5,6,7}; //方法三
    }
}

💬代码解释:

  • array1、array2、array3 都是局部变量,需要在Java虚拟机栈上开辟空间进行存储。
  • 同时它们也都是引用变量,该变量中存放着其指向的对象的地址

二. 引用变量是什么?

  • 引用变量又叫引用,可以理解成一个指针。
  • 创建一个引用就相当于创建了一个保存了地址的变量,这个地址就是这个变量的值。
  • 如果进行数组参数传参,其实就相当于将数组的地址传入到所对应函数的形参中,避免整个数组的拷贝(万一数组比较长,空间会浪费很多)。

三. 对象是什么?

  • 对象的一个实例,人,狗,书本等都可以叫做对象。
  • array1指向的对象就是1到6这六个数字。
  • 对象需要在上开辟空间进行存储。

在这里插入图片描述

注意:

  • 在初始化数组的时候,数据类型后面的 [ ] 中不可以添加任何数字。初始化数组后,编译器会自动知晓数组元素的个数。
  • 习惯将 [ ] 放在数组名称的前面,因为 [ ] 也是数据类型的一部分。尽管也可以写成类似 int array[] = {1,2,3};这样的代码形式。
  • 在静态初始化的时候,数组元素个数和初始化数据的格式应当保持一致
  • 日常的数组使用以方法一为主

二、打印数组内容

方法一:利用循环结构打印

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5,6};
        System.out.println("数组元素个数为:" + array1.length);
        for (int i = 0; i < array1.length; i++) {
            System.out.print(array1[i] + " ");
        }
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

  • array.length能够获取到数组的长度
  • 使用 [ ] 来访问到各个下标的数组元素,下标有效范围 [0 , array.length - 1]
  • 不能超出有效范围,否则会出现数组越界异常(ArrayIndexOutOfBoundsException)

方法二:增强for循环(for-each 循环)

📑代码示例:

public class TestDemo {
    public static void print(int[] array) {
        for(int x : array) {
            System.out.print(x + " ");
        }
    }
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5,6};
        print(array1);
    }
}

🏸 代码结果

在这里插入图片描述

💬代码解释:

  • for( : ) 括号里冒号的右侧是需要遍历的数组名称,左侧是数组里面存放的元素的类型定义的变量。向变量x中存放一个,接着打印一个,从而实现遍历数组内容的功能。
  • 和方法一相比,方法二没有办法利用下标拿到各个元素的内容,从而进行更多的操作,但是能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错。

在这里插入图片描述

  • 利用 print 函数打印数组,传参时,将 array1的值(即其指向的对象的地址)传给了 print 函数,用数组 array 接收,此时 array 数组也指向了地址为 0x888 的那个对象,便可以将其打印。

  • 这意味着,array 函数拥有改变地址为 0x888 的那个对象的内容

方法三:利用toString()方法

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5,6};
        System.out.println(Arrays.toString(array1));
    }
}

🏸 代码结果

在这里插入图片描述

💬代码解释:

一. 这里调用 Arrays 这个操作数组的工具类里面的 toString() 方法,该方法可以将当前的数组转换为字符串的形式进行输出

二. 想要使用这个类,首要做的就需要导入包(就相当于C语言当中的#include < >),这里需要导入Java的实用工具类库java.util包,具体导入代码为 import java.util.Arrays;

三. 什么是包?
程序员进行开发并不是从最底层一步一步的完完全全的由自己实现,为了减少代码量,提高效率,我们需要站在巨人的肩膀上进行开发,包里面有着大量的编译好生成字节码文件,拿来就可以在jdk中运行。导包实际上就是导入这个包底下的类。

  • import java.util.*表示把 util 包下的所有类都导入到程序当中,但是实际上并不会导入所有东西到文件里,因为在Java中用到了哪个类才会加载哪个类。
  • import java.util.Arrays表示将util包下特定的类Arrays导入到程序当中。

自我实现toString()方法

public class TestDemo {
    public static String myToString(int[] array) {
        if(array == null) {
            return null;
        }
        if(array.length == 0) {
            return "";
        }
        String ret = "[";
        for (int i = 0; i < array.length  ; i++) {
            ret += array[i];
            if(i < array.length - 1) {
                ret += ",";
            }
        }
        ret += "]";
        return ret;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        String ret = myToString(array);
        System.out.println(ret);
    }
}

💬代码解释:

  • 将数组转换成字符串的形式输出,通过 ‘+’ 将字符进行拼接,需要注意的是,数组最后一个元素(下标为array.length - 1)后面是没有逗号的

  • 还需要注意实参是 null实参是一个不包含任何元素的数组这两种情况。考虑完善,使方法更加完善。

拓展:交换整形变量

写一个方法使其具有交换两个整形变量的功能

思路一:

📑代码示例:

public class TestDemo {
    public static void swap(int a,int b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        swap(x,y);
        System.out.println("x = " + x + " y = " + y);
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

结果显而易见,并没有实现数据的交换,思路一的思路是有问题的。

因为对于 int 这样的基础类型,形参 a 和 b 就是实参 x 和 y 的临时拷贝,即传值调用。

就是说,你a b交换了和我x y有啥关系!

思路二:

利用数组进行交换

📑代码示例:

public class TestDemo {
    public static void swap(int[] array) {
        int tmp = array[0];
        array[0] = array[1];
        array[1] = tmp;
    }
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int[] array1 = {a,b};
        System.out.println("交换前: " + array1[0]  + " " + array1[1]);
        swap(array1);
        System.out.println("交换后: " + array1[0] + " "  + array1[1]);
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在这里插入图片描述

三、了解null

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5,6};
        int[] array2 = array1;
        int[] array3 = null;
    }
}

💬代码解释:

  • 在此处,array2 这个引用指向 array1 这个引用所指向的对象
  • array3 这个引用赋值一个null,代表着不指向任何对象
  • 一个引用类型如果不知道将来指向哪个对象,就可以将其赋值为 null
  • 当引用类型赋值为 null 时,如果通过 null 尝试访问属性的时候,例如想要求数组的长度时(array3.length),将会报错,显示空指针异常(NullPointerException)

四、牛刀小试

练习一:元素 * 2

题目:写一个方法, 将数组中的每个元素都 * 2(要求不改变原来的数组)

📑代码示例:

public class TestDemo {
    public static int[] change(int[] array) {
        int[] ret = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            ret[i] = array[i] * 2;
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4,5};
        System.out.println("改变前array1数组:" + Arrays.toString(array1));
        int[] ans = change(array1);
        System.out.println("改变后array1数组:" + Arrays.toString(array1));
        System.out.println("改变后返回的数组:" + Arrays.toString(ans));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

  • 将 array1作为参数传给change()函数,由于数组是引用类型,返回的时候只是将 array1指向的对象的地址递给了实参 array ,并没有拷贝数组的内容,从而比较高效。
  • 在 change() 函数中重新创建了一个数组 ret 来接收array数组中每个数乘以2后的结果,返回数组 ret,就不会破坏原有的数组。
  • change() 函数结束后,数组 array 和 数组 ret 随之被销毁,但是其指向的对象在堆上并不会销毁。

在这里插入图片描述

练习二:数组拷贝

方法一:循环拷贝

📑代码示例:

public class TestDemo {
    public static int[] arraysCopy(int[] array) {
        int[] newArray = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            newArray[i] = array[i];
        }
        return newArray;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        int[] ret = arraysCopy(array);
        System.out.println(Arrays.toString(ret));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在 arraysCopy 这个拷贝的方法中定义一个新的数组 newArray 通过 for 循环的方式来进行拷贝,最后将拷贝的数组返回

方法二:Arrays.copyOf 和 Arrays.copyOfRange

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        int[] newArray = Arrays.copyOf(array,2 * array.length);
        System.out.println(Arrays.toString(newArray));
        int[] newArray2 = Arrays.copyOfRange(array,1,4);
        System.out.println(Arrays.toString(newArray2));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在这里插入图片描述

  1. 如果新的数组(newArray)的长度大于需要拷贝的数组(array)的长度,用进行补充。
  2. Java中一般看到 from to 这样的一个范围,一般情况下都会是左闭右开,在这里从打印下标为 1 的元素进行打印,打印到下标为 4 的元素为止(不包括下标为 4 的元素),范围为 [1,4)。

方法三:System.arraycopy

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        int[] newArray = new int[array.length];
        System.arraycopy(array,1,newArray,2,3);
        System.out.println(Arrays.toString(newArray));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在这里插入图片描述

在该代码中从原数组(array)下标为 1 的位置开始拷贝,拷贝到目标数组(newArray)下标为 2 的位置,拷贝的长度为 3,剩余的位置补 0 。

方法四:array.clone

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6};
        int[] newArray = array.clone();
        System.out.println(Arrays.toString(newArray));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在这里插入图片描述

深拷贝与浅拷贝

以上举例的四种拷贝的实现方式都属于深拷贝,那么什么是浅拷贝?两者又有什么区别?

在这里插入图片描述

提问:如果想要将上述的浅拷贝变成深拷贝,应当怎么做?

唯一的办法就是将 array1 数组中每个元素指向的对象在堆中全部复制一份,拷贝之后的数组中的元素指向新复制的对象。

在这里插入图片描述

当面试的时候,面试官问,这几种拷贝方式是深拷贝还是浅拷贝?

应当这么回答:

首先思考拷贝的对象是什么?

  • 对象是基本数据类型,是深拷贝
  • 对象是引用类型
    • 只是单纯的使用这几个方法,是浅拷贝
    • 使用这几个方法,并且代码本身也处理了深拷贝,是深拷贝

练习三:查找数组中指定元素

顺序查找

📑代码示例:

ublic class TestDemo {
    public static int findNum(int[] array,int key) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == key) {
                return i;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
       int[] array = {23,35,2,31,5,67};
       int ret = findNum(array,31);
       if (ret == -1) {
           System.out.println("查无此数");
       }else {
           System.out.println("该数下标为: " + ret);
       }
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

顺序查找采用的是最暴力的通过循环一个一个对比进行查找的方法,思想简单。最大的缺点是效率低下,如果该数组中有1000个元素,刚好查找的元素位于该数组的最后,就意味着循环需要进行1000次

二分查找

二分查找针对的是有序数组

📑代码示例:

public class TestDemo {
    public static int binarySearch(int[] array,int key) {
        int left = 0;
        int right = array.length-1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] > key) {
                right = mid - 1;
            }else if (array[mid] < key) {
                left = mid + 1;
            }else {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
       int[] array = {23,35,2,31,5,67};
       Arrays.sort(array);
       System.out.println("排序后的数组:" + Arrays.toString(array));
       int ret = binarySearch(array,5);
       if (ret == -1) {
           System.out.println("查无此数");
       }else {
           System.out.println("下标为: " + ret);
       }
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

首先要用 Array 这个类中的 sort 这个方法对数组进行排序,使之成为有序数组。

在这里插入图片描述

练习四:冒泡排序

📑代码示例:

public class TestDemo {
    public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            boolean flag = true;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                    flag = false;
                }
            }
            if (flag) {
               break;
            }
        }
    }
    public static void main(String[] args) {
        int[] array = {8,3,5,7,2};
        System.out.println("排序之前" + Arrays.toString(array));
        bubbleSort(array);
        System.out.println("排序之后" + Arrays.toString(array));
    }
}

🏸 代码结果:

在这里插入图片描述

💬代码解释:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

冒泡排序是一种简单基础的排序算法,实现原理是重复排序数组序列,并比较每一对相邻的元素。

  1. n个元素时,一共将进行 n - 1 趟冒泡排序。

  2. i (1 ~ n - 1)趟排序需要进行 n - 1 - i 次比较。每当元素顺序不正确时(在这里实现的是升序),就进行交换。

  3. 为了提高效率,在每次进入一趟新的排序时,定义一个 flag 变量,初始化为 true。如果某一趟冒泡排序,有进行交换这一操作,就将 flag 赋值为 false ;如若一整趟冒泡排序下来没有进行交换元素,flag 始终为true ,说明该数组已经排序完成,就可以提前跳出循环,完成一整个排序过程。

完!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-26 11:59:28  更:2021-08-26 12:00:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 8:45:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码