《Thanking in Java》16. 数组

16.1 数组为什么特殊

数组与其他种类的容器之间的区别有三方面:效率、类型和保存基本类型的能力。在java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。为这种速度所付出的代价就是数组对象的大小被固定,并且在其生命周期中不可改变。

数组之所以优于泛型之前的容器,就是因为你可以创建一个数组去持有某种具体类型。这意味着你可以通过编译器检查,来放置插入错误类型和抽取不当类型。

随着自动包装机制的出现,容器已经可以与数组几乎一样方便地用于基本类型中了。数组硕果仅存的有点就是效率。

16.2 数组是第一级对象

基本类型的数组如果是数值型,就被自动初始化为0;如果是字符型,就被自动转化为O;如果是布尔型,就被自动初始化为false。

16.3 返回一个数组

在java中,你只是直接“返回一个数组”,而无需担心要为数组负责——只要你需要它,它就会一直存在,当你使用完后,垃圾回收器会清理掉它。

16.4 多维数组

Arrays.deepToString方法可以将多维数组转换为多个String。

数组中构成矩阵的每个向量都可以具有任意的长度,这被称为粗糙数组,其每个对象列表的长度都是不相等的。

public class RaggedArray {
    public static void main(String[] args) {
        Random rand = new Random(47);
        // 3-D array with varied-length vectors:
        int[][][] a = new int[rand.nextInt(7)][][];
        for(int i = 0; i < a.length; i++) {
            a[i] = new int[rand.nextInt(5)][];
            for(int j = 0; j < a[i].length; j++)
                a[i][j] = new int[rand.nextInt(5)];
        }
        System.out.println(Arrays.deepToString(a));
    }
}

自动包装机制对数组初始化也起作用。

16.5 数组与泛型

数组与泛型不能很好地组合,不能实例化具有参数化类型的数组。但是可以参数化数组本身的类型。

class ClassParameter {
  public T[] f(T[] arg) { return arg; }
}

class MethodParameter {
  public static  T[] f(T[] arg) { return arg; }
}

public class ParameterizedArrayType {
  public static void main(String[] args) {
    Integer[] ints = { 1, 2, 3, 4, 5 };
    Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 };
    Integer[] ints2 =
      new ClassParameter().f(ints);
    Double[] doubles2 =
      new ClassParameter().f(doubles);
    ints2 = MethodParameter.f(ints);
    doubles2 = MethodParameter.f(doubles);
  }
}

使用参数化方法而不使用参数化类的方便之处在于:你不必为需要应用的每种不同的类型都使用一个参数去实例化这个类,并且你可以将其定义为静态的。

尽管不能创建实际的持有泛型的数组对象,但是可以创建非泛型的数组,然后将其转型:

public class ArrayOfGenerics {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        List[] ls;
        List[] la = new List[10];
        ls = (List[]) la; // "Unchecked" warning
        ls[0] = new ArrayList();
        // Compile-time checking produces an error:
        //! ls[1] = new ArrayList();

        // The problem: List is a subtype of Object
        Object[] objects = ls; // So assignment is OK
        // Compiles and runs without complaint:
        objects[1] = new ArrayList();

        // However, if your needs are straightforward it is
        // possible to create an array of generics, albeit
        // with an "unchecked" warning:
        List[] spheres =
                (List[]) new List[10];
        for (int i = 0; i < spheres.length; i++)
            spheres[i] = new ArrayList();
    }
}

16.6 创建测试数据

Arrays.fill()方法,只能用同一个值填充各个位置,而针对对象而言,就是复制同一个引用进行填充。

另一种方式就是创建一个Generator接口,在泛型类中实现其next方法,然后通过泛型方法将泛型类toArray返回。

16.7 Arrays实用功能

Arrays.asList接收任意的序列或数组作为其参数,并将其转变为List容器。

System.arraycopy()用它复制数组比用for循环复制要快很多,不会自动包装和自动拆包,两个数组必须具有相同的确切类型。

Arrays.equals()数组相等的条件是元素个数必须相等,并且对应位置的元素也相等。数组相等是基于内容的,通过Object.equals()比较。

class CollectionData extends ArrayList {
    public CollectionData(Generator gen, int quantity) {
        for (int i = 0; i < quantity; i++)
            add(gen.next());
    }

    // A generic convenience method:
    public static  CollectionData list(Generator gen, int quantity) {
        return new CollectionData(gen, quantity);
    }
}

class Generated {
    // Fill an existing array:
    public static  T[] array(T[] a, Generator gen) {
        return new CollectionData(gen, a.length).toArray(a);
    }

    // Create a new array:
    @SuppressWarnings("unchecked")
    public static  T[] array(Class type,
                                Generator gen, int size) {
        T[] a =
                (T[]) java.lang.reflect.Array.newInstance(type, size);
        return new CollectionData(gen, size).toArray(a);
    }
}

public class CompType implements Comparable {
    int i;
    int j;
    private static int count = 1;

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

    public String toString() {
        String result = "[i = " + i + ", j = " + j + "]";
        if (count++ % 3 == 0)
            result += "\n";
        return result;
    }

    public int compareTo(CompType rv) {
        return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
    }

    private static Random r = new Random(47);

    public static Generator generator() {
        return new Generator() {
            public CompType next() {
                return new CompType(r.nextInt(100), r.nextInt(100));
            }
        };
    }

    public static void main(String[] args) {
        CompType[] a = Generated.array(new CompType[12], generator());
        System.out.println("before sorting:");
        System.out.println(Arrays.toString(a));
        Arrays.sort(a);
        System.out.println("after sorting:");
        System.out.println(Arrays.toString(a));
    }
}

java重排序算法针对基本类型是快速排序,针对对象类型是稳定归并排序。无需担心排序的性能,除非你可以证明排序部分的确是程序效率的瓶颈。

如果数组已经排好序了,就可以使用Arrays.binarySearch()执行快速查找。 如果找到目标返回值等于或大于0,如果数组包含重复的元素,则无法保证找到的是这些副本中的哪一个。

应该优选容器而不是数组,只有在已证明性能成为问题时,才应该讲程序重构为使用数组。

你可能感兴趣的:(《Thanking in Java》16. 数组)