Thking in Java---数组详解

虽然学习java也有几个月了,但是发现自己对java数组的使用还是显得不够熟练,比如说一个比较简单的自己指定对象数组的排序规则(类似于在C++中的重载sort()的comp()函数)就不知道怎么写。另外java数组的一些常用函数也不是很熟悉。所以借着学习这一章的机会,好好的做个总结。不过值得注意的是在集合框架中支持泛型后,我们应该尽量使用ArrayList而不是数组了。下面以小样例的形式展示常见的数组用法。

1).数组的多种初始化方式
下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象。值得注意的是:对象数组和普通数组的各种操作基本上都是一样的;要说有什么不同的话就是对象数组默认值为null,而基本数组视本身情况而定。

package lkl;

import java.util.Arrays;

///下面演示了数组的初始化
//包括对象数组和普通数组
class Base{
    private static long counter;
    private final long id = counter++;
    public String toString(){
        return "Base"+id;
    }
}


public class ArrayOptions {

    public static void main(String[] args){
        Base[] a; ///只声明数组而不分配空间
        Base[] b = new Base[5]; ///分配空间,默认初始化为null
        System.out.println("b: "+Arrays.toString(b));

        Base[] c=new Base[4];
        for(int i=0;i<c.length;i++){
            if(c[i]==null){
                c[i]=new Base();
            }
        }

        Base[] d={new Base(),new Base(),new Base()}; ///聚集初始化语法(隐式的使用new)

        a=new Base[]{new Base(),new Base(),new Base()}; //动态聚集初始化

        System.out.println("a.length= "+a.length);
        System.out.println("b.length= "+b.length);
        System.out.println("c.length= "+c.length);
        System.out.println("d.lenght= "+d.length);
        a=d;
        System.out.println("a.lenght= "+a.length);


        int[] e; 
        int[] f=new int[7]; ///分配空间以后会默认初始化为0
        System.out.println(Arrays.toString(f));

        int[] g=new int[4];
        for(int i=0;i<g.length;i++){
            g[i]=i*i;
        }
        int[] h={1,2,3,4};

        //没有初始化就引用length,编译错误
        //System.out.println("e.lenght= "+e.length);

        System.out.println("f.length= "+f.length);
        System.out.println("g.length= "+g.length);
        System.out.println("h.length= "+h.length);

        e=h;
        System.out.println("e.length= "+e.length);
        e=new int[]{3,4,3};
        System.out.println("e.length= "+e.length);
   }/*Output
        b: [null, null, null, null, null]
        a.length= 3
        b.length= 5
        c.length= 4
        d.lenght= 3
        a.lenght= 3
        [0, 0, 0, 0, 0, 0, 0]
        f.length= 7
        g.length= 4
        h.length= 4
        e.length= 4
        e.length= 3
   */
}

2). 从函数中返回一个数组
对于这种情况,如果在C/C++中我们可能会选择返回一个指针,因为不可能直接返回一个数组。但是在java里允许我们直接返回一个数组(当然实际上来讲还是一个引用);并且使用完了后我们也不需要手动的销毁数组,垃圾回收器会帮助我们进行清理。所以返回一个数组和返回一个普通值就没什么两样了。

package lkl;

import java.util.*;

///返回一个数组
///函数可以显示的声明返回一个数组
///这与返回其它类型并没有什么不同,实际上都是返回一个引用
//我们也不需要去手动的释放这个数组的内存,在适当的时候垃圾回收器会情理掉它的
public class ReturnArray {

    private Random rand = new Random();
    public int[] fun(int n){ ///返回一个具有大小为n的int类型数组
        int[] a=new int[n];
        for(int i=0;i<n;i++)
            a[i]=rand.nextInt(1000);
        return a;
    }

    public static void main(String[] args){

       ReturnArray ret = new ReturnArray();
        for(int i=1;i<=10;i++){
            System.out.println(Arrays.toString(ret.fun(i)));
        }
    }/*
        [627]
        [181, 296]
        [337, 415, 233]
        [617, 607, 956, 897]
        [985, 847, 965, 620, 302]
        [955, 727, 320, 328, 741, 631]
        [298, 532, 478, 116, 448, 718, 754]
        [722, 531, 491, 183, 560, 410, 657, 20]
        [95, 9, 491, 277, 431, 401, 556, 101, 871]
        [600, 311, 221, 761, 9, 689, 664, 40, 167, 636]
    */
}

3).多维数组
java中的多维数组和C++中是类似的。

package lkl;

import java.util.Arrays;
import java.util.*;

///多维数组的创建和初始化
public class Multi {

    public static void main(String[] args){
        int[][] a={{1,2,3},{4,5,6}};///花括号可以分隔每维

        int[][] b=new int[2][4]; ///默认初始化为0
        Base[][] c = new Base[2][4]; ///对于对象数组也没有什么不同

        ///deepToString()方法可以将多维数组转换成多个String
        System.out.println(Arrays.deepToString(a));

        System.out.println(Arrays.deepToString(b));
        System.out.println(Arrays.deepToString(c));


        ///数组中构成矩阵的每个向量都可以具有任意的长度
        Random rand = new Random(56);

        int[][][] d=new int[rand.nextInt(8)][][];
        for(int i=0;i<d.length;i++){
            d[i]=new int[rand.nextInt(5)][];
            for(int j=0;j<d[i].length;j++){
                 d[i][j]=new int[rand.nextInt(4)];
            }
        }
        System.out.println(Arrays.deepToString(d));
    }
    /*Output
         [[1, 2, 3], [4, 5, 6]]
         [[0, 0, 0, 0], [0, 0, 0, 0]]
         [[null, null, null, null], [null, null, null, null]]
         [[[0], [0, 0, 0], [0]], [[0], []], [[], [0]], [], [[0, 0], [0, 0, 0], [0, 0], [0, 0]]]
      */
}

4).数组的填充函数Arrays.fill()
我们可以调用这个函数使用指定的值对数组进行填充,但是这种填充和C++中的memset()不一样,它不是整块的内存复制,实际上仍是使用for循环进行赋值。这就导致我们可以指定任意的填充值,但是相应的其效率就不是那么令人满意了。这个函数适用与各种数组,并且还有一个重载版本可以让我们指定填充的起点和终点。

package lkl;

import java.util.Arrays;

public class Functiion {

    public static void main(String[] args){

        //fill()函数可以允许我们用一个固定的值来进行填充
        ///它的另一种重载形式可以运行我们指定填充的起点和终点
        int[] a=new int[10];
        Arrays.fill(a, 1); 
        System.out.println(Arrays.toString(a));
        Arrays.fill(a, 0, 4, 2); ///这种格式调用时注意起点包括但终点不包括
        Arrays.fill(a, 4,10,4);
        System.out.println(Arrays.toString(a));
    }/*Output
          [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
          [2, 2, 2, 2, 4, 4, 4, 4, 4, 4]
        */
    }

5).数组的整体复制和比较
数组的整体复制使用的不是Arrays包下的函数而是System包下的arraycopy()函数。arraycopy()函数所需要的参数有:源数组,表示从源数组复制的起始位置,目的数组,表示目的数组开始赋值的起始位置,需要复制元素的个数。因为这个函数底层使用的整块内存的一个复制,所以效率上要比for循环好很多。
另外,对于对象数组我们复制的只是对象的引用,而不是对象本身的拷贝。并且这个函数也不会自动的执行自动包装和拆包,所以两个数组必须要有相同的类型。

package lkl;

import java.util.Arrays;

//System类提供了一个arraycopy()函数用来进行数组的复制
//这种复制比for循环快的多
//arraycopy须要的参数有:源数组,表示从源数组开始复制的位置,
///目的数组,复制到目的数组的位置,以及复制元素的个数
///另外arraycopy()不会自动包装和自动拆包,在复制对象数组时复制的只有
//引用,而不是对对象进行拷贝
public class ArrayCopy {

    public  static void main(String[] args){

        int[] a=new int[4];
        int[] b=new int[6];
        Arrays.fill(a, 10);
        Arrays.fill(b, 20);
        System.out.println("b= "+Arrays.toString(b));
        System.arraycopy(a, 0, b, 2, 3);
        System.out.println("b= "+Arrays.toString(b));

        Integer[] c=new Integer[10];
        Integer[] d=new Integer[6];
        Arrays.fill(c, new Integer(3));
        Arrays.fill(d, new Integer(5));
        System.out.println("c= "+Arrays.toString(c));
        System.arraycopy(d, 1, c, 2, 3);
        System.out.println("c= "+Arrays.toString(c));
    }/*Output
       b= [20, 20, 20, 20, 20, 20]
       b= [20, 20, 10, 10, 10, 20]
       c= [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
       c= [3, 3, 5, 5, 5, 3, 3, 3, 3, 3]
    */
}

Arrays包下提供了equals()方法用于整个数组的比较。两个数组相等的标准是:具有相同的尺寸,并且对应位上的元素相等(通过对每一个元素使用equals()作比较来进行判断)。如果数组中装的是我们自己定义的对象引用,那么要确保重写了equals()方法,否则比较就会出错。

package lkl;

import java.util.Arrays;

//用于判断数组是否相等的方法
///Arrays.equals()
//两个数组相等的定义为数组的尺寸相同并且对应位元素相同

class MyClass{
    private int i;
    public MyClass(int i){
        this.i=i;
    }
    @Override//重写equals()函数
    public boolean equals(Object t){
        if(t==null) return false;
        if(t.getClass()!=MyClass.class)
            return false;
        return  ((MyClass)t).i==this.i;
    }
}

public class ArrayEquals {

    public static void main(String[] args){

        int[] a1=new int[10];
        int[] a2=new int[10];
        Arrays.fill(a1, 11);
        Arrays.fill(a2, 11);
        System.out.println(Arrays.equals(a1, a2));
        a1[2]=10;
        System.out.println(Arrays.equals(a1, a2));

        //如果是我们自己定义类型的数组就有可能出问题了
        ///必须要自己重写equals()函数,提供相等的标准
        MyClass[] m1=new MyClass[4];
        MyClass[] m2=new MyClass[4];
        Arrays.fill(m2, new MyClass(3));
        Arrays.fill(m1, new MyClass(3));
        System.out.println("m1==m2? "+Arrays.equals(m1,m2));
    }/*
        true
        false
        m1==m2? true
    */
}

6).自定义元素的比较规则
有时候我们需要自己定义数组元素的比较规则,以便进行排序和查找等操作。java提供了两种方法来提供比较的功能。一种是实现java.lang.Comparable接口,使你的类具有天生的比较能力,此接口很简单,只有compareTo()一个方法。此方法接收另一个Object为参数,如果当前对象小于参数则返回负值,相等返回0,大于返回正值。下面的类实现了Comparable接口,我们用排序函数进行检验。

package lkl;
import java.lang.*;
import java.util.*;
//有时候我们需要自定义如何比较对象
//为此有两种方法可以实现,第一种就是实现java.lang.Comparable接口
//另一种方法就是实习Comparator接口

public class CompType implements Comparable<CompType>{
     int i;
     int j;

    public CompType(int n1,int n2){
        i=n1; j=n2;
    }

    public String toString(){
        return "[ i= "+i+" j= "+j+"]";
    }

    //先按i的大小排序,i相等就按j大小排序
    ///实现Comparable接口就必须实现CompareTo()方法
    ///compareTo()的返回值只有三种,-1,0,1,分别表示小于,等于,大于
    public int compareTo(CompType T){
        if(i==T.i){
            return (j<T.j?-1:(j==T.j?0:1));
        }
        return (i<T.i?-1:(i==T.i?0:1));
    }

    public static void main(String[] args){
        Random rand = new Random(45);
        CompType[] ct=new CompType[4];
        for(int i=0;i<4;i++)
             ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));

        System.out.println("排序之前: ");
        for(int i=0;i<4;i++){
            System.out.println(ct[i]);
        }
        Arrays.sort(ct);

        System.out.println("\n排序之后:");
        for(int i=0;i<4;i++){
            System.out.println(ct[i]);
        }
    }/*
        排序之前: 
        [ i= 9 j= 3]
        [ i= 1 j= 8]
        [ i= 7 j= 3]
        [ i= 1 j= 3]

        排序之后:
        [ i= 1 j= 3]
        [ i= 1 j= 8]
        [ i= 7 j= 3]
        [ i= 9 j= 3]
    */
}

第二种方法就就是创建一个实现了Comparator接口的类,将其一个对象作为比较标准传入排序函数中。这个类有compare(),equals()两个方法。因为每个类都会继承Object类的equals()方法,所以一般情况下我们只需要实现compare()作为比较标准就行了。

package lkl;

import java.util.*;

///自己实现Comparator接口,定义排序标准,实现compare()函数
///排序时传入一个Comparator对象
///下面的定义表示的是处理CompType类型的比较
class MyComparator implements Comparator<CompType>{
    public int compare(CompType t1,CompType t2){
        if(t1.i==t2.i){
            return (t1.j<t2.j?-1:(t1.j==t2.j?0:1));
        }
        return (t1.i<t2.i?-1:(t1.j==t2.j?0:1));
    }
}

public class ComapratorTest {

    public static void main(String[] args){
        Random rand = new Random(45);
        CompType[] ct=new CompType[4];
        for(int i=0;i<4;i++)
             ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));
        System.out.println("排序之前: ");
        for(int i=0;i<4;i++){
            System.out.println(ct[i]);
        }
        Arrays.sort(ct,new MyComparator());
        System.out.println("\n排序之后:");
        for(int i=0;i<4;i++){
            System.out.println(ct[i]);
        }
    }
}

7).数组的排序和查找
使用内置的排序方法,可以对任意的基本类型数组进行排序,也可以对任意的对象数组进行排序。只要该对象实现了Comparable接口或则是具有相关联的Comparator。对于查找,只要数组已经排好序,我们就可以使用Arrays.binarySearch()执行快速的二分查找。如果查找到了该元素,返回其位置。如果返回的是一个负值,说明数组中不包含此元素,这个负值还表示该元素应该插入的位置。值得注意的是,如果使用Comparator排序了某个对象数组(基本数组是不能用Comparator进行排序的),那么在对其进行二分时也要传入一个相同的Comparator对象(使用一个重载函数版本)。

package lkl;
import java.util.*;

///数组的排序与查找问题
///java中内置了排序算法,可以对各种类型的数组进行排序
///只要该对象实现了Comparable接口或具有相关联的Comparator
///java标准类库中的排序算法对正排序的特殊类型进行了优化---针对基本类型设计的
//“快速排序”以及针对对象设计的稳定归并排序

///对排序以后的数组我们可以直接使用内置的二分查找
//如果使用了Comparator排序了某个对象数组(基本类型数组无法使用Comparator进行排序)
//在使用binarySearch()时也需要提供同样的
public class ArraySort {

    public static void main(String[] args){
        Random rand= new Random(330);
        int[] a=new int[10];
        for(int i=0;i<10;i++)
            a[i]=rand.nextInt(20);
       int t=a[4];
        System.out.println("排序前: ");
        System.out.println(Arrays.toString(a));
        Arrays.sort(a);
        System.out.println("排序后:");
        System.out.println(Arrays.toString(a));

        System.out.println("二分查找"+t+"的位置为为:"+Arrays.binarySearch(a, t));


        ///下面是对前面对象数组二分的演示
        CompType[] ct=new CompType[4];
        for(int i=0;i<4;i++)
             ct[i]=new CompType(rand.nextInt(10),rand.nextInt(12));
        CompType T =ct[2];
        System.out.println("T= "+T);
        Arrays.sort(ct,new MyComparator());
        System.out.println("排序之后");
        for(int i=0;i<4;i++){
            System.out.println(ct[i]);
        }
        System.out.println("T的位置为"+Arrays.binarySearch(ct, T,new MyComparator()));
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值