第十六章:数组

数组

  • 数组我相信大家都很熟悉吧。数组的优点就是随机读取速度快,像ArrayList就是使用了数组作为数据存储格式而实现的。但是缺点也很明显,它的大小是固定的。ArrayList之所以能够任意大小,是因为使用了size,扩容的时候拷贝数组到新数组。本章我们就来讨论一下数组的特点和用法。

数组是第一级对象

  • 无论使用哪种类型的数组,数组标识符其实只是一个引用,指向堆中创建的一个真实对象,这个(数组)对象用以保存指向其他对象的引用。除了new表达式显式创建数组对象,还可以用一些隐式语法。length是它的唯一一个可以访问的成员属性,并且是只读的。数组的类型既可以是引用类型,也可以是基本类型。如果是基本类型的话,都会有默认值。而引用类型则为null

多维数组

  • 我们这里来说一下粗糙数组。通常我们会这么定义多维数组:
    int[][] test = new int[5][5];
  • 这样相当于是定义了一个5*5的矩阵。但是如果说,我想定义一个不规则大小的数组怎么办?那么我们就可以这么写:
    int[][] test = new int[5][];
    test[0] = new int[1];
    test[1] = new int[2];
    test[2] = new int[3];
    test[3] = new int[4];
    test[4] = new int[5];
  • java允许我们先确定最外层数组的大小,然后再逐步确定里层数组大小。这种写法比较少用,这里就做个笔记。

数组和泛型

  • 通常,数组与泛型不能很好的结合。你不能实例化具有参数化类型的数组:
List<String>[] ls = new List<String>[10];//编译错误
  • 但是我们可以通过一些人为控制来完成这个数组的创建。我们先来回忆一下泛型的使用。
public class People {
    public static void main(String args[]) {
        Holder<Integer> ho1 = new Holder<Integer>();
        Holder<String> ho2 = new Holder<String>();

        Holder ho11 = ho1;
        Holder ho22 = ho2;
        Holder<String> ho111 = ho11;
        Holder<Integer> ho222 = ho22;
        ho111.set(new String("1"));
        ho222.set(new Integer(1));

        System.out.println(ho111.get());
        System.out.println(ho222.get());
    }
}
class Holder <T> {
    T obj;
    T get() {
        return obj;
    }
    void set(T obj) {
        this.obj = obj;
    }
}
----------------运行结果
1
1
  • 你也许会以为new Holder<Integer>()new Holder<String>()是两种不同的类型。但是事实告诉我们,我们试图往前者插入String,不会导致任何错误。这是为什么呢?因为在Holder内部,它只认为泛型是它的擦除边界Object而已。而真正能够限制我们的是ho111认为的泛型类型(在编译期间)。我们当然不会这么写代码,此处也是为了让我们深刻的理解泛型的作用。再次强调,在泛型类内部,所有的泛型类型是擦除边界!
  • 那么此处我们应该就这样去定义这个数组:
List<String>[] ls = new List[10];
  • 你不要觉得new List[10];相比于new List<String>[10];有什么区别,他们本质是没有区别的,这个在上面已经讨论过了。只要我们List<String>[] ls这么做了,编译器就能够为我们有效检查ls元素的类型。虽然会有警告,但是大家可以放心的这么写。

Arrays实用功能

  • 先来讲一下Arrays.equals()Arrays.deepEquals()。前者是针对一维数组的,会比较其每个元素的equals()方法,后者是针对多维数组的。
import java.util.Arrays;

public class People {
    public static void main(String args[]) throws Exception {
        int[] a = new int[10];
        int[] b = new int[10];
        //System.out.println(a.equals(b));//false 比较引用
        System.out.println(Arrays.equals(a, b));//true
        a[0] = 1;
        System.out.println(Arrays.equals(a, b));//false
        int[][] c = new int[1][];
        int[][] d = new int[1][];
        a = new int[1];
        b = new int[1];
        c[0] = a;
        d[0] = a;
        System.out.println(Arrays.equals(c, d));//true
        System.out.println(Arrays.deepEquals(c, d));//true
        d[0] = b;
        System.out.println(Arrays.equals(c, d));//false
        System.out.println(Arrays.deepEquals(c, d));//true
        System.out.println();
        c[0][0] = 1;
        System.out.println(Arrays.equals(c, d));//false
        System.out.println(Arrays.deepEquals(c, d));//false
    }
}
  • sort()是用于排序的,binarySearch()是对已经有序的数组进行二分法查询元素。toString()产生数组的字符串表示,fill()用于填充单一元素内容。还有其他用法就不过多介绍了。

复制数组

  • System.arraycopy()是一个用于复制(浅拷贝)数组的方法,它的速度比用循环复制要快得多。它需要的参数有:源数组,源数组偏移量,目标数组,目标数组偏移量,复制个数。另外需要注意的是该方法不会执行自动包装和自动拆包,两个数组必须具有相同的确切类型。来看下面的例子:
import java.lang.reflect.Field;
import java.util.Arrays;

public class People {
    public static void main(String args[]) throws Exception {
        int[] a = new int[10];
        int[] b = new int[10];
        Arrays.fill(a, 1);
        Arrays.fill(a, 2);
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
        //System.arraycopy(a, 5, b, 0, b.length);//a越界会产生异常,切记
        //System.arraycopy(a, 0, b, 5, b.length);//b越界会产生异常,切记
        System.arraycopy(a, 5, b, 5, b.length - 5);
        System.out.println(Arrays.toString(b));
        Integer[] c = new Integer[10];
        Arrays.fill(c, new Integer(3));
        System.out.println(Arrays.toString(c));
        //System.arraycopy(a, 5, c, 5, c.length - 5);//不会自动包装和拆包,会产生异常,切记
        Integer[] d = new Integer[10];
        Arrays.fill(d, new Integer(4));
        /*for (int i=0; i<d.length; i++) {
            d[i] = new Integer(4);
        }*///这种写法针对下面的浅拷贝测试只改变d[0]
        System.arraycopy(d, 0, c, 5, d.length - 5);
        System.out.println(Arrays.toString(c));

        //为了验证是浅拷贝:
        //接下来我打算用反射改变d[0]的值,当然也可以使用其他引用类型做测试,我这里就不改了
        Field f = Integer.class.getDeclaredField("value");
        f.setAccessible(true);
        f.set(d[0], 520);//因为d[0]到d[9]其实是同一个对象,所以都会改变
        System.out.println(Arrays.toString(d));
        System.out.println(Arrays.toString(c));//c也变了,说明的确是浅拷贝
        //更简单的判断方式:
        System.out.println(new Integer(520) == new Integer(520));//false
        System.out.println(d[0] == c[5]);//true
    }
}
----------------------------运行结果
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 2, 2, 2, 2]
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
[3, 3, 3, 3, 3, 4, 4, 4, 4, 4]
[520, 520, 520, 520, 520, 520, 520, 520, 520, 520]
[3, 3, 3, 3, 3, 520, 520, 520, 520, 520]

sort

  • Arrays.sort是一个很常用的方法,直接对int排序或者String排序,我相信大家都会,那如果我要对一个自定义的数组排序,该如何呢?此时我们需要使这个类实现Comparable接口,否则直接调用sort会出现CCE异常(因为其内部使用了Comparable接口的方法)。
import java.util.Arrays;
import java.util.Comparator;

public class People {
    public static void main(String args[]) throws Exception {
        Test[] test = new Test[5];
        for (int i = 0; i < test.length; i++) {
            test[i] = new Test(i, i + "" + i);
        }
        System.out.println(Arrays.toString(test));
        Arrays.sort(test);
        System.out.println(Arrays.toString(test));

        // 假设我不满意这个排序方式,又无法直接修改Test类:
        Arrays.sort(test, new Comparator<Test>() {
            public int compare(Test o1, Test o2) {
                //实际上String也实现了Comparable
                return o1.s.compareTo(o2.s);
            }
        });
        System.out.println(Arrays.toString(test));
    }
}
class Test implements Comparable<Test> {
    int i;
    String s;
    public Test(int i, String s) {
        this.i = i;
        this.s = s;
    }
    //Comparable 需要实现的方法
    public int compareTo(Test o) {
        return o.i - i;//逆序
    }
    //方便打印输出
    public String toString() {
        return i + "->" + s;
    }
}
-------------------执行结果:
[0->00, 1->11, 2->22, 3->33, 4->44]
[4->44, 3->33, 2->22, 1->11, 0->00]
[0->00, 1->11, 2->22, 3->33, 4->44]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值