请选择 进入手机版 | 继续访问电脑版

石家庄老站长

点击联系客服
客服QQ: 客服微信:
 找回密码
 立即注册
查看: 5|回复: 0

分析[Java]数组的使用

[复制链接]

1

主题

1

帖子

-7

积分

限制会员

积分
-7
发表于 2021-8-27 15:13:37 | 显示全部楼层 |阅读模式
文章目录

一、创建数组2、打印数组内容的方法1:使用循环结构打印方法2:使用for循环增强(for-each循环)方法3:使用toString()方法直接扩展toString()方法实现:交换结构化变量3,null

相同类型的变量集合,如果指定了名称。

数组的名称。组成阵列的个别变数称为阵列的元素。

(本文画的画的地址都是捏造的,理解其意义就可以了。)

一、创建数组

基本语法格式:

//静态初始化

数据类型[]数组名称={数据初始化};

//动态初始化

数据类型[]数组名称=new  int[元素数];

数据类型[]数组名称=new数据类型[] {初始化数据
en punctuation">};

📑代码示例:

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 + " ");
            }
        }
    }

    🏸 代码结果:



    💬代码解释:


  • 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;
                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 = array * 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 = array;
            }
            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));
        }
    }

    🏸 代码结果:



    💬代码解释:




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

    方法三: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 == 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));
        }
    }

    🏸 代码结果:



    💬代码解释:









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


    [ol]
  • 有 n个元素时,一共将进行 n - 1 趟冒泡排序。
  • 第i (1 ~ n - 1)趟排序需要进行 n - 1 - i 次比较。每当元素顺序不正确时(在这里实现的是升序),就进行交换。
  • 为了提高效率,在每次进入一趟新的排序时,定义一个 flag 变量,初始化为 true。如果某一趟冒泡排序,有进行交换这一操作,就将 flag 赋值为 false ;如若一整趟冒泡排序下来没有进行交换元素,flag 始终为true ,说明该数组已经排序完成,就可以提前跳出循环,完成一整个排序过程。
    [/ol]

    完!
  • 回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|无图版|手机版|小黑屋|石家庄@IT精英团

    GMT+8, 2021-10-17 23:11 , Processed in 0.218400 second(s), 25 queries .

    Powered by Discuz! X3.4

    © 2001-2021 Comsenz Inc.

    快速回复 返回顶部 返回列表