• Java 数组


    数组。

    1.数组是一种引用类型,不是基本的数据类型。

    2.数组是一种简单的数据结构,线性的结构。

    3.数组是一个容器,可以用来存储其他元素。

    数组是可以存储任意数据类型的元素。

    4.数组分为:一维数组,二维数组,三维数组,多维数组... 

    5.数组中存储的元素类型统一。

    6.数组长度不可以改变,数组一旦创建,元素不可改变,是固定的。结合以下例子来看,

    public class ArrayTest01{
     public static void main(String[] args){
     int[] a={100,200,150,300};  // 这种方式称为“静态初始化一维数组”
       boolean[] b={true,false,true,true};
       String[] strs={"ABC","CS","DAD"};
       byte[] t={1,2,3};
     char[] c={'q','a','c'};
     //Object类型数组
     Object o1=new Object();
     Object o2=new Object();
     Object o3=new Object();
     //数组存的是每个对象在堆里的内存地址,不是对象。
     Object[] objs={o1,o2,o3};
     }
    }

    上面的代码声明了不同类型的数组,并且对数组进行了初始化。我们可以结合下面这张图来理解。

    从上面的图可以看出,数组拿首元素的内存地址作为数组对象的内存地址,并且里面存的是每一个对象在堆中的内存地址。 此外,数组中每一个元素都是有索引的,从0开始,任何一个数组都有一个length属性来获取数组中元素的个数,数组最后一个元素的下标是数组的长度减1。那么我们就知道了取得数组的第一个元素即a[0],最后一个元素即a[a.length-1]。

    使用数组的优点是什么呢?数组中存储元素的类型是统一的,每一个元素在内存中所占的空间大小是相同的,知道数组的首元素的内存地址,要查找的元素只要知道下标就可以快速地计算出偏移量,通过首元素内存地址加上偏移量快速计算出要查找元素的内存地址,通过内存地址快速定位该元素,所以数组查找元素的效率较高。

    当然了,它的缺点就是增删元素的效率很低。随意地对数组进行增删元素,当增加元素的时候,为了保证数组数组中元素在空间存储上是有序的,所以被添加元素位置后面的所有元素都要向后移动,删除元素也是,后面所有的元素都要向前移动,因此数组的增删元素的效率很低。

    接下来看初始化一维数组的方式,有两种,静态初始化和动态初试化,静态初始化很简单,上面的代码中已经解释说明了,那么如何进行动态初试化一维数组呢?看下面例子,

    public class ArrayTest03{
     public static void main(String[] args){
       //动态声明一个int类型的数组,最多可以存储4个元素
       int[] a1=new int[4];
       //遍历
       for(int i=0;i<a1.length;i++){
         System.out.println(a1[i]);
       }
       //赋值
       a1[0]=1;
       a1[1]=2;
       a1[2]=3;
       a1[3]=4;
       //赋值之后遍历
       for(int i=0;i<a1.length;i++){
         System.out.println(a1[i]);
       }
     }
    }

    编译运行后输出:

    0
    0
    0
    0
    1
    2
    3
    4

    从上面的代码可以看出,未给数组赋值之前动态初试化数组后遍历输出为0,这是因为,动态初始化一维数组会在堆内存中分配这个数组,并且数组中每一个元素都采用默认值,而int类型的默认值为0.给数组赋值之后,遍历输出数组值才改变。

    知道了静态初始化和动态初试化,那么如何进行初始化方式的选择?

    1.无论是动态初始化还是静态初始化,最终的内存分布都是一样的;

    2.如果在创建数组的时候,知道数组中应该存储什么数据,这个时候采用静态初始化方式;

    如果在创建数组的时候,无法预测到数组中存储什么数据,只是先开辟空间,则使用动态初始化方式。

    深入了解一维数组。

    public class ArrayTest05{
     public static void main(String[] args){
       Animal[] as=new Animal[4];  
       Cat c1=new Cat();
       Cat c2=new Cat();
       Dog d1=new Dog();
       Dog d2=new Dog();
       as[0]=c1;
       as[1]=c2;
       as[2]=d1;
       as[3]=d2;
       //遍历数组,取出每个对象,如果是Cat类,执行move()方法,如果是Dog()类,执行eat()方法。
       for(int i=0;i<as.length;i++){
         Animal a=as[i];
         //强制类型转换,向下转型,用instanceof判断a所属的类型
         if (a instanceof Cat){
           Cat c=(Cat)a;
           c.move();
         }else if(a instanceof Dog){
           Dog d=(Dog)a;
           d.eat();
         }
       }
     }
    }
    class Animal{}
    class Cat extends Animal{
     public void move(){
       System.out.println("Cat move");
     }
    }
    class Dog extends Animal{
     public void eat(){
       System.out.println("Dog eat");
     }
    }

    编译运行后输出:

    Cat move
    Cat move
    Dog eat
    Dog eat

    在上述代码中,我们首先动态声明一个Animal类型的数组,并且最多可以存储4个元素,之后new出两个Cat类型的对象c1,c2,两个Dog类型的对象d1,d2,并将它们赋值给Animal类型的数组as,接着使用一个for循环遍历数组,遍历数组,取出每个对象,如果是Cat类,执行move()方法,如果是Dog()类,执行eat()方法。其中用到了向下转型中的instanceof判断父类对象是否属于某一个子类。上述代码是一个比较综合的小程序,可以顺便回忆一下之前讲过的内容。

    接下来我们看看在方法调用中如何传递数组,看以下程序:

    /*在方法调用中传递数组*/
    public class ArrayTest06{
     public static void main(String[] args){
       int[] a={1,2,12,3,3};
       m1(a);
       System.out.println("---------------");
       m1(new int[]{3,42,4,5535});
     }
     public static void m1(int[] a){
       for (int i=0;i<a.length;i++){
         System.out.println(a[i]);
       }
     }
    }

    编译运行后输出:

    1
    2
    12
    3
    3
    ---------------
    3
    42
    4
    5535

    从以上代码中可以看出,在方法调用中传递数组可以用两种方式,一种是先定义一个数组int[] a={1,2,12,3,3};然后将a作为参数在m1()方法中调用,另一种是直接在参数的位置new一个数组,也就是m1(new int[]{3,42,4,5535});的形式。

    最后来看看数组的拷贝,即数组的扩容方式。其实数组的拷贝,在JDK中已经提供了写好的方法,直接调用即可,看以下代码:

    public class ArrayTest08{
     public static void main(String[] args){
       //System.arraycopy(Object src,int srcPos,int destPos,int length);
       //System.out.println(源数组,原数组的开始下标,目标数组,目标数组的开始下标,拷贝的长度);
      int[] src={2,3,4,5,6,7,8};
     int[] dest={10,11,12,13,14,15,16};
     //把src中的4,5,6拷贝到dest数组从13开始
     System.arraycopy(src,2,dest,3,3);
     //遍历
     for(int i=0;i<dest.length;i++){
       System.out.println(dest[i]);
     }
     }
    }

    编译运行后输出:

    10
    11
    12
    4
    5
    6
    16

    二维数组是一个特殊的一维数组,特殊在这个一维数组中每一个元素都是“一维数组”。结合以下代码了解一下二维数组的静态初始化方法。

    public class ArrayTest09{
     public static void main(String[] args){
       //静态初始化二维数组
       int[][] a={
                {1,2,3},
                {45,34},
              {0},
              {10,23,85,99}
       };
           //以上这个数组有多少个一维数组
           System.out.println(a.length+"个一维数组");
           //获取第一个一维数组
           int[] a0=a[0];
           int a00=a0[0];
           System.out.println(a00);
       //获取第一个一维数组的第一个元素
           System.out.println(a[0][0]);  
           //获取最后一个一维数组中的最后一个元素
           System.out.println(a[3][3]);
           System.out.println(a[a.length-1][a[a.length-1].length-1]);  
         //遍历
       //纵向循环在外
       for(int i=0;i<a.length;i++){
         for(int j=0;j<a[i].length;j++){
           System.out.print(a[i][j]+" ");
         }
           System.out.println();
       }
       }
    }

    编译运行后输出:

    4个一维数组
    1
    1
    99
    99
    1 2 3
    45 34
    0
    10 23 85 99

    上述代码中实现了二维数组的静态初试化以及二维数组的遍历方法,原理跟之前讲的一维数组的一样,只要把二维数组当作特殊的一维数组就可以了。每一句代码前面都跟着注释,理解了之前的一位数组,二维数组的相关方法也是很容易理解的。

    接下来看看二维数组的动态初始化,结合以下代码来看。

    /*
    关于二维数组的动态初始化
    */
    public class ArrayTest10{
     public static void main(String[] args){
       //3个一维数组,每个一维数组中有4个元素
       int[][] a=new int[3][4];
       //遍历
       for(int i=0;i<a.length;i++){
         for(int j=0;j<a[i].length;j++){
           System.out.print(a[i][j]+" ");
         }
         System.out.println();
       }
                   System.out.println("----------");
       //赋值
       a[1][2]=10;
       //遍历
       for(int i=0;i<a.length;i++){
         for(int j=0;j<a[i].length;j++){
           System.out.print(a[i][j]+" ");
         }
         System.out.println();
       }
     }
    }

    编译运行后输出:

    0 0 0 0 
    0 0 0 0 
    0 0 0 0 
    ----------
    0 0 0 0 
    0 0 10 0 
    0 0 0 0

    从以上代码看出,数组初始化未赋值之前每个元素的默认值为0。二维数组进行遍历的时候依靠两个for循环来实现,并且纵向循环在外,也就是写for循环的时候外层循环指的是二维数组中有几个一维数组,之后在内层循环中实现一维数组的元素的读取。上面的代码对二维数组进行遍历之后,对数组中的第一行第二列的元素重新赋值,之后重新遍历输出数组中的每一个元素值。

    最后看看在方法调用中传递二维数组的方式。

    public class ArrayTest11{
     public static void main(String[] args){
       //在方法调用中传递数组
       m1(new int[][]{{1,2,2},{23,232,24},{343,45}});
     }
     public static void m1(int[][] a){
       for(int i=0;i<a.length;i++){
         for(int j=0;j<a[i].length;j++){
           System.out.print(a[i][j]+" ");
         }
       System.out.println();
       }
     }
    }

    跟一维数组的实现方式一样,只不过要注意二维数组的写法。

    搜索微信公众号“程序员考拉”,欢迎关注!
  • 相关阅读:
    并发技术
    体系结构基础
    TCP协议总结
    HTTP协议总结
    SQL Cookbook:使用字符串
    模运算的基本性质
    682. Baseball Game
    编程之法:面试和算法心得(字符串的全排列)
    编程之法:面试和算法心得(最长回文子串)
    编程之法:面试和算法心得(回文判断)
  • 原文地址:https://www.cnblogs.com/naihuangbao/p/9444547.html
Copyright © 2020-2023  润新知