栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

数组相关 内容 解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数组相关 内容 解析

文章目录
  • 什么是 数组?
  • 创建数组 的 三种方式
    • 1.静态 初始化
    • 2.动态 初始化
  • 数组的使用
  • 遍历 数组
  • java 中 的 引用
  • 数组拷贝
    • 1.循环拷贝
    • 2.通过 java提供 的方法 拷贝
  • 数组 克隆
  • 注意: 深浅拷贝
  • 二维数组
    • 1、二维数组 的创建

数组

什么是 数组?

数组 : 可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。

这里 就可以 拿 车库 来举例子:


在java中,包含6个整形类型元素的数组,就相当于上图中连在一起的6个车位,从上图中可以看到:

  1. 数组中存放的元素其类型相同

  2. 数组的空间是连在一起的

  3. 每个空间有自己的编号,其实位置的编号为0,即数组的下标 、

另外 : int[] array 这里的 int[] 相当 于 是 一个 数据类型(整形数组)。 array 相当于 一个变量 , 这里就 是 创建了 一个变量 array 他的 数据 类型 是 整形 数组,

这里我们有了 数组就能 解决 一下 不必要的麻烦,

如: 这里我们 要 为 六个 同学 记录 他们的 年龄信息 , 如果 创建 变量,就会 显得 频繁 这里 就可以直接使用 数组来创建。

下面就来 了解 一下 如何 创建数组。

创建数组 的 三种方式

1.静态 初始化

基本 语法 : 数据类型[] 数据名称 = {初始化 数据};

代码演示:

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5,6};
}

这里 就 提出一个问题, 整形 数组 能不能存储 其他类型的 数据呢?

举例: 创建 一个 整形数组,存储 浮点型 , 可以明显看到这里报错了。


注意事项: 静态初始化的时候, 数组元素个数和初始化数据的格式是一致的(依据初始化元素的数量,来决定数组的大小)

另外 : 我们 整形 数组 是可以 存储 字符类型的



这里 字符的 本质 是 ASCII 码值 , 我们就可以 通过 整形 数组 来存放。

静态 初始化看完,我们来看 动态。

2.动态 初始化

基本语法: 数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };

public class TheDefinitionAndUseOfArrays {
    public static void main(String[] args) {
        int[] array = new int[]{1,2,3,4,5,6};//其表达意思与作用与第一种方法相同,就是创建一个数组,并初始化数据
       
    }
}


注意:

两个[]中,都不能有数字的存在

new 是java中的一个关键字,其作用是实例化一个对象 ,

这里就 意味着 Java 当中的 数组 也是 一个 对象。

对象 会在 类和 对象中 ,讲 ,这里 不要急 。先知道 这个东西。

这里我们 动态初始化 还 有一种 :

基本 语法: 类型[] 数组名 = new 类型[元素个数];

注意: 这种 写法, 没有 初始 化 数组, 会默认 分配 0 给数组。

public class TheDefinitionAndUseOfArrays {
    public static void main(String[] args) {
        int[] array = new int[6];
        // 该写法表达的意思是 现在有一个数组,长度为6,注意数组的元素,并没有对其初始化,在Java中,默认6个元素都是0
        // 等价于 int[] array={0,0,0,0,0,0},
    }
}

另外 : 在Java中的数组 也可 写成 c 一样 int arr[] ,但 一般 不推荐 这么 写 。

数组的使用

获取数组长度: 通过 数组名 .length

在 java 中 没有和 c 语言 的 sizeof ,但 是 有 比 sizeof 更加 方便的 length , 它能够 制动获取 数组的长度(元素个数),十分方便,这里我们就来看一下。


这里 就 求出了 数组的 长度。

获取 某下表的 元素

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int[] arr = new int[10];
    for(int i = 0;i
        arr[i] = i;
    }
    int n = sc.nextInt();
    int ret = arr[n];
    System.out.println(ret);
}



这里我们 来 看一下 数组 越界 异常 , 多看 看 错误 信息, 在 后面 讲到 异常的 时候 能 更好 理解。

了解 数组 越界 异常

这里 我们 还是 那 上面的 代码 , 我们 输入 11 ,数组的 大小 为 10 这里 就 会 越界, 的

错误 信息 : java.lang.ArrayIndexOutOfBoundsException 这里 的 11 就算 错误 的 地方。

遍历 数组

1. for 循环 遍历 数组

2. 增强for 循环 , for - each循环 遍历 数组

这里 我们 来 区别 一下 for 和 for - each

for 循环 可以 拿到 下标, 而 增强for 循环 拿不到 下标, 这就算最大 的 区别,当我们 只要元素 值 ,而不需要 下标是 就可以 使用 增强for 循环。

另外 for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.

3. 通过 java 中 写 好的类 Arrays

Arrays.toString : toString 将当前的数组元素,转换成字符串形式,并将其返回(也可以说 将参数的数组,以字符串形式进行输出)

Arrays.toString 讲 数组 变为 字符串 输出 , 我们 可以 用 一个 字符串 类型 的 变量来接收, 这里 也可以 直接打印, 为了 好看这里就没有 直接 打印。

java 中 的 引用

在 了解 引用 之前我们 先来 简单的 了解 一下 jvm 的 内存分布:

内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:

  1. 程序运行时代码需要加载到内存
  2. 程序运行产生的中间数据要存放在内存
  3. 程序中的常量也要保存
  4. 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁
    如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:

因此JVM也对所使用的内存按照功能的不同进行了划分:

程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址

(假设 写代码的时候, 中途 有事 退出,程序计数器,就会 帮你 记住 当前 写 的 位置,等你 回来的 时候,就能够 告诉你 )

虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含

这里 java虚拟机栈 就 先当与 我们 常说 的 栈帧

有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。

本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的

堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。

(堆 一般 存放 的 是 对象)

方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域

稍微 了解 一下 即可 ,下面我们继续 回到 数组。

这里我们 的数组 就是 引用类型, 存放 的 堆区 的 一块 空间 (地址)。

这里 引用 很像 指针, 这里 我们 就来 谈谈 他们的 相同之处,和 区别, 引用 和指针 都是 存放 地址, 但是 指针 能够后 解引用,而 引用 不能 解引用, 这里 就 能够 更好的证明, 为啥java 中没有 传 址 ,而 c 能够 传地址。

那么 这里就 会 有 另外 一个问题, c语言中的 指针 能够 赋值 NULL ,那么java 中 的引用 能不能 赋值 NULL呢 ?

其实 是 可以的 但是 java 中的 NULL是 小写的 null。 这里 只是简单的 举例 c 和 java 中的 引用 和 指针 的 相同 和 区别,c 中的 指针 和 java 中的 引用 还是 有 很大 区别的。

演示:

这里 就表示 数组 不 指向 任何 地方,打印 数组 ,自然为空。

如果 这里 想要 求 数组 长度呢?


这 里 就 报错了,来 看看

错误 信息 : java.lang.NullPointerException ,空指针异常, 这里 我们的 引用都为空何来的 长度,这里就是 错误。要注意。


下面我们 就 了解一下 数组 传参

1.通过 方法打印 数组


这里我们就来画图 来看看 是 如何 讲 数组 当作 参数 传递 的 。

下面图简化了 :

下面我们 来 看 两个 题目 , 有助于 更好的 理解 引用、

思考一下 这里 两次 打印 的 结果 为 ?

可以看到 我们 的 func2 将 数组 改变了 ,而 func 却 没有 ,为啥呢 ?

请看:

第一步:


第二步:



第三步:


下面我们 来 谈谈 引用 能否 同时 指向 多个 对象



这里我们 打印 数组 的长度 发现 是 5 ,这里 我们 也 印证 引用 不能 指向 多个 对象,如果 多次给 引用 赋值 对象 ,那么 引用 指向 会 是 最后 那个赋值 的 对象

结论: 一个引用只能指向一个对象(一个引用只能保存一个对象的地址)

讲了 这么 久 , 那么 这里 有个 疑问 引用 都是 在 栈 上的 吗?

答案是不一定的,因为一个变量在不在栈上,是你变量的性质决定的

如果你的引用是一个局部变量,那就一定在栈上 实例成员变量那就不一定了。(有关于 成员变量 的 知识 ,会 在 类和 对象 中 讲到)。

局部变量的引用保存在栈上, new 出的对象保存在堆上. 堆的空间非常大, 栈的空间比较小.因为 堆 是整个 JVM 共享一个, 而 栈 每个线程具有一份 (一个 Java 程序中可能存在多个栈)


填坑 : 使用 数组 交换 两个变量

下面我们 就来 填坑, 还记得 在方法中 ,讲过 2种交换 变量 的 值 ,其中 之一 就是 通过 数组,那么我们 就来 操作 一下。


这里 就通过 数组 交换了 两个 变量的 值。

数组 除了 当 参数 也可以 当作方法 的返回值,这里 我们就来看一下。

数组作为返回值:

题目: 不改变 原有的 数组情况 下, 将 数组 扩大 两倍

如果 我们 这样写 就会 破坏 原有 的 数组。

那么 我们 就可以 在 方法中 ,创建 一个 新 的 数组,

new 一个 大小 为 arr.length 的 数组 ,即便 方法 结束 ,栈区的 空间 被销毁 但 我们 通过 返回 一个 数组 ,将 在 堆区 新开辟 的 地址 给 返回 了 过来。


做完 这个练习 ,我们 一直 用人家 的 toString 方法,那么 我们 就来自己 实现 一下 自己 的 toString方法

MytoString


我们 可以看到 通过 toString 打印出来的 数组 是

[ + ,(逗号) + 数字 +, 逗号 +] ,这里我们 就可以通过 之前我们学过的String 拼接 ,在 一开始 添加 一个[ 然后 拼接 上我们 的 数组 元素,

在拼接 上 , (逗号 )


注意: 这里 我们 可以看到 最后 一个 元素没有 , (逗号) 这里 我们 就 需要 添加 条件 . 那么 要添加 什么 条件呢 , 其实 也非常 简单 ,这里我们 只需最后 一个 元素 不添

加 逗号 那么 当 i < arr.length - 1 就需要 添加 逗号 而 i > arr.length - 1 我们 就不要 添加 逗号.

那么代码 就可以 这么 写:


实现完 我们自己 的 toString 方法. 接下来 来 几道 练习 题 来 更好 的熟悉数组.


题目 一 : 找数组中的最大元素

题目 很简单 这里我们 就 直接 遍历数组 然后 一个 一个比较 找出 最大值.

    public static int max(int[] arr){
        if(arr == null){
            // 这里 我们 arr 如果为空 我们直接返回 -1, 后面
            //学到 异常时我们 可以 通过 抛出异常。
            return -1;
        }
        int max = 0;
        for(int i = 0;i
            if(max > arr[i]){
                //找到 大于 当前max 更新 max;
                max = arr[i];
            }
        }
        return max;
    }
    public static void main(String[] args) {
        int[] arr = {2,3,5,1,11,4,66,55};
       int max  =  max(arr);
        System.out.println(max);
        //为了 体现 方法的 返回值,我们 这样写 ,也可以直接打印。
    }

题目二 : 查找数组中指定 元素

这题 也 非常简单 我们 还是 遍历 数组 就可以 直接 完成

    public static int find(int[] arr,int n ){
        //规定一下 如果 没有找到 返回 -1
        for(int i = 0;i
            if(arr[i] == n) {
                return i;
            }
        }
        // 走到 这里 循环结束 还没有找到 返回 -1、
        return -1;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // n 为 要查找 的 元素。
        int n = sc.nextInt();
        int[] arr = {2,3,5,1,11,4,66,55};
        int ret = find(arr,n);
        System.out.println(ret);
    }


这里我们 改一下题目 ,我们 只需确定 数组 中 是否 含有某个元素,不需要返回其下标.

那么我们这里就可以 使用 for - each 来 完成 (通过 for 循环 也可以 ,主要加深点 for - each 印象)

public static boolean find2(int[] arr,int n){
    for(int x : arr){
        if(x == n){
            return true;
        }
    }
    return false;
}

这里 返回类型 就可以 改为 boolean ,直接 返回 真 或 假 .

题目 三: 二分查找


注意 二分 查找 是 针对 有序 数组 的 , 每次 查找 会 减少 一半 他 的时间 复杂度 就为 O(log N) ,如果 不知道 时间复杂度这里 会在 数据结构中 学习到,这里 知道 有 这么 个东西 即可.

知道了 大致流程 我们就来 完成我们 的 代码:

 public static int BinarySearch(int[] arr, int n) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] < n) {
                // 此时 说明 n 在 mid 的 右边
                left = mid + 1;
            } else if (arr[mid] > n) {
                //此时 说明 n 在 mid 的 左边
                right = right - 1;
            } else {
                // 此时 说明 找到了
                return mid;
            }
        }
        //如果 left >  right 那么 就说明 没有找到
        return -1;
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int ret = BinarySearch(arr,5);
        System.out.println(ret);
    }


这里 完成了 我们的 二分查找.

补充: 其实 在 java中 我们 可以通过 Arrays 这个 类, 来 调用 java中 只带 的二分查找,

语法: Arrays.binarySearch()

 public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int ret = Arrays.binarySearch(arr,5);
        System.out.println(ret);
    }

题目 三 : 判断 数组 是否有序 (这里 按照 升序)

直接 上代码 也不难 还 是 针对 拿 几种 遍历 数组 来 展开.

如果 是 这样 写 就要 注意:

这里 报错 了 了 为啥 呢 ? 这里我们 要注意 的就是 i < arr.lengt 当 i == arr.lengt - 1 是 arr[ i + 1] 是不是 就 越界 了 , 这里 我们 就 需要 将 条件

改为 i < arr.length - 1 即可 .

将 i < arr.length 改为 i < arr.length - 1

题目 四 : 冒泡 排序

这里 偷个懒 : 可以 看一下 我 排序 的博客 里面 就 通过 图 的 形式 将 冒泡 排序 完成 了 , 还有 其他 几大排序 ,如果觉得 难 等 学到 ,在 看 也行,现在 只看 冒泡 排序 即可 : 上 链接 :排序(sort)

可以 看到这 里 我们就 完成 了 冒泡 排序, 下面 我们 在来看 一下 优化 .

    public static void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            boolean flg = true;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    flg = false;
                }
            }
            if (flg) {
                break;
            }

        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 44, 21, 2, 45, 66, 1, 2};
        System.out.println(Arrays.toString(arr));
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }

这里我们 添加 了 一个 flg 判断 ,如果 没有 进入 if 语句 就 说明 数组 已经 是 有序的 .

补充: 这里我们 java 也是 有排序 的 ,但 不是 基于 冒泡 排序 实现 的 ,这里冒泡 排序 的 时间 复杂度 不管 有没有 优化 都是 O(N^2) 太慢了.

下面 就来 看一下


这里 就 通过 Arrays.sort 完成我们 的排序

下面再来 补充 一个 Arrays 类 中 的方法 fill

fill 单词 的 意思 是 填满 , 这里 就是 将 数组 填满 成 某个元素

演示:

可以 看到我们 默认 值 0 全部 变为 了 520 .

另外 Arrays.fill 也是 可以 指定 填充 范围 的 .

演示:

注意: 在 java 中 很多 带范围 的 都是 左 闭 右开 的结构.


下面 继续

题目 五 : 数组逆序

通过 两个 指针 一个 指向下标 0 一个 指向 下标 arr.length - 1 , 交换即可 知道 left = right 那么 就 说明 逆序 完成.

    public static void reversed(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--;
        }
    }

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

题目 六 : 数组数字排列

给定一个整型数组, 将所有的偶数放在前半部分, 将所有的奇数放在数组后半部分 例如 {1, 2, 3, 4} 调整后得到 {4, 2, 3, 1}

沿着 上一题 思路 : 我们 只需要 在 前面找到 为 奇数 的 放在与 后面 找到 为 偶数 的 交换 即可

    public static void swap(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;
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        swap(arr);
        System.out.println(Arrays.toString(arr));
    }

题目 看完是不是 对 数组有 很 深刻 的理解 了,那么 下面我们来看一下 数组 的 几种 拷贝

数组拷贝

1.循环拷贝


直接 通过 for 循环 来拷贝.

2.通过 java提供 的方法 拷贝

java 提供 了 拷贝 方法 Arrays.copyOf

这里我们 打开 java 的 帮助 手册 ,可以 看到 copyOf 需要 的参数 ,这里 我们就来使用 一下.

另外 :


我们 拷贝 的 长度 大于 数组 原来 的 长度 ,copyOf 就 会 帮我们 扩容 , 元素 补 0 , 注意 : 后面 商用 copyOf 完成我们的 扩容 操作

除了 copyOf ,我们 可以 使用 copyOfRange

下面 就来 演示 一下:

这里 就 指定 了 0 到 5 下标 的元素 拷贝. 同样 5 是 没有 包含 的 这里 也 是 左闭 右开 .

最后我们 来 看一下 , c / c ++ 实现的 一个 方法 .

System.arraycopy ,

这里 我们 可以 通过 Ctrl + 鼠标左键 来 查看 他 的 源码

看到 native 就说明 他是 被 c / c++实现 的 ,我们 如果 在 Ctrl + 鼠标左键 点击 arraycopy 是查看 不了的 .

另外 : native 方法 是 由 c++ 、c 实现的优点快

数组 克隆


语法: 数组名. clone().

注意: 深浅拷贝

学完 拷贝 其实 会有 一个 问题 , 深浅 拷贝 ,这里 面试 是 会 被问到 .

下面我们 就来看 一下.

深拷贝 : 拷贝完成 后 通过一个引用来修改 拷贝 的数组 ,原来的数组不会受影响 就为 深拷贝

浅拷贝 :拷贝完成 后 通过 一个引用来修改 拷贝的数组,原来的数组一同被 修改就为 浅拷贝

注意 :大多情况 下 拷贝的是对象 ,深拷贝 或 浅拷贝 都是 人为实现 的

一维数组 差不多 就 这么 多 知识 ,下面 我们 来 看一下二维数组.

二维数组

1、二维数组 的创建

基本语法 :

1.数据类型[][] 数组名 = { 初始化数据 }

2.数据类型[][] 数组名 = new 数据类型[][]{ 初始化数据 }

3.数据类型[][] 数组名 = new 数据类型[行数] [列数]


演示 : 创建 一个两行 三列的 二维数组

    public static void main(String[] args) {
        不能这么写,需要自己定义行和列
        int[][] array = {1,2,3,4,5,6}; error
         
        int[][] array = {{1,2,3},{4,5,6}};
        
        int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
        
        int[][] array3 = new int[2][3];
    }
}


一般 我们 认为 二维 数组 是 这样 的 ,但java 中 的 二维数组 相对于 c 又有点 不同 ,c 语言 中 的 二维 数组 ,是连续的,而 java 中 的 二维 数组 其实 是 这样的

下面我们 就来 对 二维 数组 打印

1.通过 for 循环 打印


这里 我们 通过 arr.length 求出列数 , 通过 arr[i].length 求出 每列 的 行数, 有没有 疑问 这里 为啥 不直接 写 arr[0].length ,而是 写 arr[i].length

不要急 ,这就是 下面 要 说 java 不一样 的 地方.


2.通过 for - each 来 打印 二维数组

注意: arr 元素的数据类型是一个一维数组,我就需要一个相同的类型的变量来接收.


还是 这 张图 ,可以 明显 的看到 我们 的 二维 数组 存放的 是一维数组的 地址 ,那么我们 通过 for - each 就 得 使用 一个 一维数组 来接收.

最后 我们 来看一下 我们的 不规则二维数组

不规则 二维 数组

演示:

注意: 这里 我们 的 列 是 可以 省略 的

如 :

那 这样 创建 有啥 意义 呢 ?

其实 这里 就 是 不规则 数组 的 精髓 我们 可以 根据 我们的 需要 设计 二维 数组 中 存放 一维数组 的 大小 .

另外 : 我们 如果 没有 给 二维数组 赋值 的 话 使用 就会出现 错误



如果 省略 掉 行 , 加 上 了 列 也会 就会报错 .

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1036643.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号