Java基础之十六 数组

数组:可以创建并组装它们,通过使用整型索引值访问它们的元素,并且它们的尺寸不能改变。

16.1 数组为什么特殊

数组与其他种类的容器之间的区别有三方面:效率,类型和保存基本类型的能力。
数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常快速。但是为这种速度所付出的代价是数组对象的大小固定,并且在其生命周期中不可改变。
ArrayList:它可以通过一个新实例,然后把旧实例中所有引用移到新实例中,从而实现更多空间的自动分配。但它的效率比数组低很多。
数组可以持有基本类型,而泛型之前的容器不能。但有了泛型,容器旧可以指定并检查它们所持有对象的类型,并且有了自动包装机制,容器看起来还能够持有基本类型。
数组和泛型容器比较:

import java.util.*;
import static net.mindview.util.Print.*;

class BerylliumSphere {
  private static long counter;
  private final long id = counter++;
  public String toString() { return "Sphere " + id; }
}
public class ContainerComparison {
  public static void main(String[] args) {
    BerylliumSphere[] spheres = new BerylliumSphere[10];
    for(int i = 0; i < 5; i++)
      spheres[i] = new BerylliumSphere();
    print(Arrays.toString(spheres));
    print(spheres[4]);

    List<BerylliumSphere> sphereList =
      new ArrayList<BerylliumSphere>();
    for(int i = 0; i < 5; i++)
      sphereList.add(new BerylliumSphere());
    print(sphereList);
    print(sphereList.get(4));

    int[] integers = { 0, 1, 2, 3, 4, 5 };
    print(Arrays.toString(integers));
    print(integers[4]);
    
    List<Integer> intList = new ArrayList<Integer>(
      Arrays.asList(0, 1, 2, 3, 4, 5));
    intList.add(97);
    print(intList);
    print(intList.get(4));
  }
} /* Output:
[Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4, null, null, null, null, null]
Sphere 4
[Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9]
Sphere 9
[0, 1, 2, 3, 4, 5]
4
[0, 1, 2, 3, 4, 5, 97]
4
*///:~

数组的优点是效率。

16.2 数组是第一级对象

无论使用哪种类型的数组,数组标识符其实只是一个引用,指向在堆中创建的一个真实对象,这个对象用以保存指向其他对象的引用。
对象数组和基本类型数组在使用上几乎是相同的。唯一区别就是对象数组保存的是引用,基本类型数组直接保存基本类型的值。

import java.util.*;
import static net.mindview.util.Print.*;

public class ArrayOptions {
  public static void main(String[] args) {
    // Arrays of objects:
    BerylliumSphere[] a; // Local uninitialized variable
    BerylliumSphere[] b = new BerylliumSphere[5];
    // The references inside the array are
    // automatically initialized to null:
    print("b: " + Arrays.toString(b));
    BerylliumSphere[] c = new BerylliumSphere[4];
    for(int i = 0; i < c.length; i++)
      if(c[i] == null) // Can test for null reference
        c[i] = new BerylliumSphere();
    // Aggregate initialization:
    BerylliumSphere[] d = { new BerylliumSphere(),
      new BerylliumSphere(), new BerylliumSphere()
    };
    print("d"+Arrays.toString(d));
    // Dynamic aggregate initialization:
    a = new BerylliumSphere[]{
      new BerylliumSphere(), new BerylliumSphere(),
    };
    // (Trailing comma is optional in both cases)
    print("a.length = " + a.length);
    print("b.length = " + b.length);
    print("c.length = " + c.length);
    print("d.length = " + d.length);
    a = d;
    print("a.length = " + a.length);

    // Arrays of primitives:
    int[] e; // Null reference
    int[] f = new int[5];
    // The primitives inside the array are
    // automatically initialized to zero:
    print("f: " + Arrays.toString(f));
    int[] g = new int[4];
    for(int i = 0; i < g.length; i++)
      g[i] = i*i;
    int[] h = { 11, 47, 93 };
    // Compile error: variable e not initialized:
    //!print("e.length = " + e.length);
    print("f.length = " + f.length);
    print("g.length = " + g.length);
    print("h.length = " + h.length);
    e = h;
    print("e.length = " + e.length);
    e = new int[]{ 1, 2 };
    print("e.length = " + e.length);
  }
} /* Output:
b: [null, null, null, null, null]
a.length = 2
b.length = 5
c.length = 4
d.length = 3
a.length = 3
f: [0, 0, 0, 0, 0]
f.length = 5
g.length = 4
h.length = 3
e.length = 3
e.length = 2
*///:~

16.3 返回一个数组

数组的清理由垃圾回收机制负责。

import java.util.*;

public class IceCream {
  private static Random rand = new Random(47);
  static final String[] FLAVORS = {
    "Chocolate", "Strawberry", "Vanilla Fudge Swirl",
    "Mint Chip", "Mocha Almond Fudge", "Rum Raisin",
    "Praline Cream", "Mud Pie"
  };
  public static String[] flavorSet(int n) {
    if(n > FLAVORS.length)
      throw new IllegalArgumentException("Set too big");
    String[] results = new String[n];
    boolean[] picked = new boolean[FLAVORS.length];
    for(int i = 0; i < n; i++) {
      int t;
      do
        t = rand.nextInt(FLAVORS.length);
      while(picked[t]);
      results[i] = FLAVORS[t];
      picked[t] = true;
    }
    return results;
  }
  public static void main(String[] args) {
    for(int i = 0; i < 7; i++)
      System.out.println(Arrays.toString(flavorSet(3)));
  }
} /* Output:
[Rum Raisin, Mint Chip, Mocha Almond Fudge]
[Chocolate, Strawberry, Mocha Almond Fudge]
[Strawberry, Mint Chip, Mocha Almond Fudge]
[Rum Raisin, Vanilla Fudge Swirl, Mud Pie]
[Vanilla Fudge Swirl, Chocolate, Mocha Almond Fudge]
[Praline Cream, Strawberry, Mocha Almond Fudge]
[Mocha Almond Fudge, Strawberry, Mint Chip]
*///:~

16.4 多维数组

对于基本类型得多维数组,可以通过使用花括号将每个向量隔开:

import java.util.*;

public class MultidimensionalPrimitiveArray {
  public static void main(String[] args) {
    int[][] a = {
      { 1, 2, 3, },
      { 4, 5, 6, },
    };System.out.println(Arrays.deepToString(a));
    System.out.println(a[0].length);
    System.out.println(a[1].length);
  }
} /* Output:
[[1, 2, 3], [4, 5, 6]]
*///:~

还可以使用new来分配数组:

import java.util.*;

public class ThreeDWithNew {
  public static void main(String[] args) {
    // 3-D array with fixed length:
    int[][][] a = new int[2][2][4];
    System.out.println(Arrays.deepToString(a));
  }
} /* Output:
[[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]]
*///:~

你可以看到基本类型数组得值在不进行显式初始化得情况下,会被自动初始化。对象数组会被初始化为null。
数组中构成矩阵得每个向量都可以具有任意得长度(粗糙数组)

import java.util.*;

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));
  }
} /* Output:
[[], [[0], [0], [0, 0, 0, 0]], [[], [0, 0], [0, 0]], [[0, 0, 0], [0], [0, 0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0], []], [[0], [], [0]]]
*///:~

用花括号把多个new表达式组织到一起:

import java.util.*;

public class MultidimensionalObjectArrays {
  public static void main(String[] args) {
    BerylliumSphere[][] spheres = {
      { new BerylliumSphere(), new BerylliumSphere() },
      { new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere() },
      { new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere() },
    };
    System.out.println(Arrays.deepToString(spheres));
  }
} /* Output:
[[Sphere 0, Sphere 1], [Sphere 2, Sphere 3, Sphere 4, Sphere 5], [Sphere 6, Sphere 7, Sphere 8, Sphere 9, Sphere 10, Sphere 11, Sphere 12, Sphere 13]]
*///:~

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

import java.util.*;

public class AutoboxingArrays {
  public static void main(String[] args) {
    Integer[][] a = { // Autoboxing:
      { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
      { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
      { 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 },
      { 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 },
    };
    System.out.println(Arrays.deepToString(a));
  }
} /* Output:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]]
*///:~

Arrays.deepToString()方法对基本类型数组和对象数组都起作用:

import java.util.*;

public class MultiDimWrapperArray {
  public static void main(String[] args) {
    Integer[][] a1 = { // Autoboxing
      { 1, 2, 3, },
      { 4, 5, 6, },
    };
    Double[][][] a2 = { // Autoboxing
      { { 1.1, 2.2 }, { 3.3, 4.4 } },
      { { 5.5, 6.6 }, { 7.7, 8.8 } },
      { { 9.9, 1.2 }, { 2.3, 3.4 } },
    };
    String[][] a3 = {
      { "The", "Quick", "Sly", "Fox" },
      { "Jumped", "Over" },
      { "The", "Lazy", "Brown", "Dog", "and", "friend" },
    };
    String[][] a4=new String[2][3];
    System.out.println("a1: " + Arrays.deepToString(a1));
    System.out.println("a2: " + Arrays.deepToString(a2));
    System.out.println("a3: " + Arrays.deepToString(a3));
    System.out.println("a3: " + Arrays.deepToString(a4));
  }
} /* Output:
a1: [[1, 2, 3], [4, 5, 6]]
a2: [[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.2], [2.3, 3.4]]]
a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The, Lazy, Brown, Dog, and, friend]]
*///:~

16.5 数组与泛型

数组与泛型不能很好的结合。你不能实例化具有参数化类型的数组。
擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。
参数化数组本身的类型:

class ClassParameter<T> {
  public T[] f(T[] arg) { return arg; }
}
class MethodParameter {
  public static <T> 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<Integer>().f(ints);
    Double[] doubles2 =
      new ClassParameter<Double>().f(doubles);
    ints2 = MethodParameter.f(ints);
    doubles2 = MethodParameter.f(doubles);
  }
} ///:~

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

import java.util.*;

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

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

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

泛型在类或方法的边界处很有效,而在类或方法的内部,擦除通常会使泛型变得不适用。
不能创建泛型数组:

public class ArrayOfGenericType<T> {
  T[] array; // OK
  @SuppressWarnings("unchecked")
  public ArrayOfGenericType(int size) {
    //! array = new T[size]; // Illegal
    array = (T[])new Object[size]; // "unchecked" Warning
  }
  // Illegal:
  //! public <U> U[] makeArray() { return new U[10]; }
} ///:~

16.6 创建测试数据

16.6.1 Arrays.fill()

只能用一个值填充各个位置,而针对对象而言,就是复制同一个引用进行填充:

import java.util.*;
import static net.mindview.util.Print.*;
public class FillingArrays {
  public static void main(String[] args) {
    int size = 6;
    boolean[] a1 = new boolean[size];
    byte[] a2 = new byte[size];
    char[] a3 = new char[size];
    short[] a4 = new short[size];
    int[] a5 = new int[size];
    long[] a6 = new long[size];
    float[] a7 = new float[size];
    double[] a8 = new double[size];
    String[] a9 = new String[size];
    Arrays.fill(a1, true);
    print("a1 = " + Arrays.toString(a1));
    Arrays.fill(a2, (byte)11);
    print("a2 = " + Arrays.toString(a2));
    Arrays.fill(a3, 'x');
    print("a3 = " + Arrays.toString(a3));
    Arrays.fill(a4, (short)17);
    print("a4 = " + Arrays.toString(a4));
    Arrays.fill(a5, 19);
    print("a5 = " + Arrays.toString(a5));
    Arrays.fill(a6, 23);
    print("a6 = " + Arrays.toString(a6));
    Arrays.fill(a7, 29);
    print("a7 = " + Arrays.toString(a7));
    Arrays.fill(a8, 47);
    print("a8 = " + Arrays.toString(a8));
    Arrays.fill(a9, "Hello");
    print("a9 = " + Arrays.toString(a9));
    // Manipulating ranges:
    Arrays.fill(a9, 3, 5, "World");
    print("a9 = " + Arrays.toString(a9));
  }
} /* Output:
a1 = [true, true, true, true, true, true]
a2 = [11, 11, 11, 11, 11, 11]
a3 = [x, x, x, x, x, x]
a4 = [17, 17, 17, 17, 17, 17]
a5 = [19, 19, 19, 19, 19, 19]
a6 = [23, 23, 23, 23, 23, 23]
a7 = [29.0, 29.0, 29.0, 29.0, 29.0, 29.0]
a8 = [47.0, 47.0, 47.0, 47.0, 47.0, 47.0]
a9 = [Hello, Hello, Hello, Hello, Hello, Hello]
a9 = [Hello, Hello, Hello, World, World, Hello]
*///:~

使用Arrays.fill()可以填充整个数组,或者只填充某个区域。

16.6.2 数据生成器

package net.mindview.util;
public class CountingGenerator {
    static char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    public CountingGenerator() {
    }
    public static class Boolean implements Generator<java.lang.Boolean> {
        private boolean value = false;
        public Boolean() {
        }
        public java.lang.Boolean next() {
            this.value = !this.value;
            return this.value;
        }
    }
    public static class Byte implements Generator<java.lang.Byte> {
        private byte value = 0;
        public Byte() {
        }
        public java.lang.Byte next() {
            return this.value++;
        }
    }
    public static class Character implements Generator<java.lang.Character> {
        int index = -1;
        public Character() {
        }
        public java.lang.Character next() {
            this.index = (this.index + 1) % CountingGenerator.chars.length;
            return CountingGenerator.chars[this.index];
        }
    }
    public static class Double implements Generator<java.lang.Double> {
        private double value = 0.0D;
        public Double() {
        }
        public java.lang.Double next() {
            double result = (double)(this.value++);
            return result;
        }
    }
    public static class Float implements Generator<java.lang.Float> {
        private float value = 0.0F;
        public Float() {
        }
        public java.lang.Float next() {
            float result = this.value;
            this.value = (float)((double)this.value + 1.0D);
            return result;
        }
    }
    public static class Integer implements Generator<java.lang.Integer> {
        private int value = 0;
        public Integer() {
        }
        public java.lang.Integer next() {
            return this.value++;
        }
    }
    public static class Long implements Generator<java.lang.Long> {
        private long value = 0L;

        public Long() {
        }
        public java.lang.Long next() {
            return java.lang.Long.valueOf((long)(this.value++));
        }
    }
    public static class Short implements Generator<java.lang.Short> {
        private short value = 0;
        public Short() {
        }
        public java.lang.Short next() {
            return this.value++;
        }
    }
    public static class String implements Generator<java.lang.String> {
        private int length = 7;
        Generator<java.lang.Character> cg = new CountingGenerator.Character();
        public String() {
        }
        public String(int length) {
            this.length = length;
        }
        public java.lang.String next() {
            char[] buf = new char[this.length];

            for(int i = 0; i < this.length; ++i) {
                buf[i] = ((java.lang.Character)this.cg.next()).charValue();
            }
            return new java.lang.String(buf);
        }
    }
}

通过反射使用这个工具:

import net.mindview.util.*;

public class GeneratorsTest {
  public static int size = 10;
  public static void test(Class<?> surroundingClass) {
    for(Class<?> type : surroundingClass.getClasses()) {
      System.out.print(type.getSimpleName() + ": ");
      try {
        Generator<?> g = (Generator<?>)type.newInstance();
        for(int i = 0; i < size; i++)
          System.out.printf(g.next() + " ");
        System.out.println();
      } catch(Exception e) {
        throw new RuntimeException(e);
      }
    }
  }
  public static void main(String[] args) {
    test(CountingGenerator.class);
  }
} /* Output:
Double: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
Float: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
Long: 0 1 2 3 4 5 6 7 8 9
Integer: 0 1 2 3 4 5 6 7 8 9
Short: 0 1 2 3 4 5 6 7 8 9
String: abcdefg hijklmn opqrstu vwxyzAB CDEFGHI JKLMNOP QRSTUVW XYZabcd efghijk lmnopqr
Character: a b c d e f g h i j
Byte: 0 1 2 3 4 5 6 7 8 9
Boolean: true false true false true false true false true false
*///:~

使用随机数生成器的Generator:

package net.mindview.util;

import java.util.Random;

public class RandomGenerator {
    private static Random r = new Random(47L);

    public RandomGenerator() {
    }

    public static class Boolean implements Generator<java.lang.Boolean> {
        public Boolean() {
        }

        public java.lang.Boolean next() {
            return RandomGenerator.r.nextBoolean();
        }
    }

    public static class Byte implements Generator<java.lang.Byte> {
        public Byte() {
        }

        public java.lang.Byte next() {
            return (byte)RandomGenerator.r.nextInt();
        }
    }

    public static class Character implements Generator<java.lang.Character> {
        public Character() {
        }

        public java.lang.Character next() {
            return CountingGenerator.chars[RandomGenerator.r.nextInt(CountingGenerator.chars.length)];
        }
    }

    public static class Double implements Generator<java.lang.Double> {
        public Double() {
        }

        public java.lang.Double next() {
            long trimmed = Math.round(RandomGenerator.r.nextDouble() * 100.0D);
            return (double)trimmed / 100.0D;
        }
    }

    public static class Float implements Generator<java.lang.Float> {
        public Float() {
        }

        public java.lang.Float next() {
            int trimmed = Math.round(RandomGenerator.r.nextFloat() * 100.0F);
            return (float)trimmed / 100.0F;
        }
    }

    public static class Integer implements Generator<java.lang.Integer> {
        private int mod = 10000;

        public Integer() {
        }

        public Integer(int modulo) {
            this.mod = modulo;
        }

        public java.lang.Integer next() {
            return RandomGenerator.r.nextInt(this.mod);
        }
    }

    public static class Long implements Generator<java.lang.Long> {
        private int mod = 10000;

        public Long() {
        }

        public Long(int modulo) {
            this.mod = modulo;
        }

        public java.lang.Long next() {
            return new java.lang.Long((long)RandomGenerator.r.nextInt(this.mod));
        }
    }

    public static class Short implements Generator<java.lang.Short> {
        public Short() {
        }

        public java.lang.Short next() {
            return (short)RandomGenerator.r.nextInt();
        }
    }

    public static class String extends net.mindview.util.CountingGenerator.String {
        public String() {
            this.cg = new RandomGenerator.Character();
        }

        public String(int length) {
            super(length);
            this.cg = new RandomGenerator.Character();
        }
    }
}

16.6.3 从Generator中创建数组

这个工具只能产生Object子类型数组,而不能产生基本类型数组:

package net.mindview.util;

import java.lang.reflect.Array;

public class Generated {
    public Generated() {
    }

    public static <T> T[] array(T[] a, Generator<T> gen) {
        return (new CollectionData(gen, a.length)).toArray(a);
    }

    public static <T> T[] array(Class<T> type, Generator<T> gen, int size) {
        Object[] a = (Object[])Array.newInstance(type, size);
        return (new CollectionData(gen, size)).toArray(a);
    }
}

使用反射动态创建具有恰当类型和数量的新数组,然后使用与第一个方法相同的技术来填充该数组:

import java.util.*;
import net.mindview.util.*;

public class TestGenerated {
  public static void main(String[] args) {
    Integer[] a = { 9, 8, 7, 6 };
    System.out.println(Arrays.toString(a));
    a = Generated.array(a,new CountingGenerator.Integer());
    System.out.println(Arrays.toString(a));
    Integer[] b = Generated.array(Integer.class,
        new CountingGenerator.Integer(), 15);
    System.out.println(Arrays.toString(b));
  }
} /* Output:
[9, 8, 7, 6]
[0, 1, 2, 3]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
*///:~

泛型不能用于基本类型,可以用生成器来填充基本类型。
创建一个转换器,它可以接受任意的包装器对象数组,并将其转化为相应的基本类型数组:

package net.mindview.util;

public class ConvertTo {
    public ConvertTo() {
    }

    public static boolean[] primitive(Boolean[] in) {
        boolean[] result = new boolean[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].booleanValue();
        }

        return result;
    }

    public static char[] primitive(Character[] in) {
        char[] result = new char[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].charValue();
        }

        return result;
    }

    public static byte[] primitive(Byte[] in) {
        byte[] result = new byte[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].byteValue();
        }

        return result;
    }

    public static short[] primitive(Short[] in) {
        short[] result = new short[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].shortValue();
        }

        return result;
    }

    public static int[] primitive(Integer[] in) {
        int[] result = new int[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].intValue();
        }

        return result;
    }

    public static long[] primitive(Long[] in) {
        long[] result = new long[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].longValue();
        }

        return result;
    }

    public static float[] primitive(Float[] in) {
        float[] result = new float[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].floatValue();
        }

        return result;
    }

    public static double[] primitive(Double[] in) {
        double[] result = new double[in.length];

        for(int i = 0; i < in.length; ++i) {
            result[i] = in[i].doubleValue();
        }

        return result;
    }
}

16.7 Arrays实用功能

Arrays类有一套用于static实用方法,它有六个基本方法:equals()比较数组,deepEquals()比较多维数组;fill()填充;sort()用于对数组排序;binarySearch()用于在排序的数组中查找元素;toString()产生数组的String表示;hashCode()产生数组的散列码。

16.7.1 复制数组

System.arraycopy()它复制数组比for快:

import java.util.*;
import static net.mindview.util.Print.*;

public class CopyingArrays {
  public static void main(String[] args) {
    int[] i = new int[7];
    int[] j = new int[10];
    Arrays.fill(i, 47);
    Arrays.fill(j, 99);
    print("i = " + Arrays.toString(i));
    print("j = " + Arrays.toString(j));
    System.arraycopy(i, 0, j, 0, i.length);
    print("j = " + Arrays.toString(j));
    int[] k = new int[5];
    Arrays.fill(k, 103);
    System.arraycopy(i, 0, k, 0, k.length);
    print("k = " + Arrays.toString(k));
    Arrays.fill(k, 103);
    System.arraycopy(k, 0, i, 0, k.length);
    print("i = " + Arrays.toString(i));
    // Objects:
    Integer[] u = new Integer[10];
    Integer[] v = new Integer[5];
    Arrays.fill(u, new Integer(47));
    Arrays.fill(v, new Integer(99));
    print("u = " + Arrays.toString(u));
    print("v = " + Arrays.toString(v));
    System.arraycopy(v, 0, u, u.length/2, v.length);
    print("u = " + Arrays.toString(u));
  }
} /* Output:
i = [47, 47, 47, 47, 47, 47, 47]
j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
k = [47, 47, 47, 47, 47]
i = [103, 103, 103, 103, 103, 47, 47]
u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47]
v = [99, 99, 99, 99, 99]
u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
*///:~

System.arraycopy()不会执行自动包装,两个数组必须具有相同的确切类型。

16.7.2 数组的比较

import java.util.*;
import static net.mindview.util.Print.*;

public class ComparingArrays {
  public static void main(String[] args) {
    int[] a1 = new int[10];
    int[] a2 = new int[10];
    Arrays.fill(a1, 47);
    Arrays.fill(a2, 47);
    print(Arrays.equals(a1, a2));
    a2[3] = 11;
    print(Arrays.equals(a1, a2));
    String[] s1 = new String[4];
    Arrays.fill(s1, "Hi");
    String[] s2 = { new String("Hi"), new String("Hi"),
      new String("Hi"), new String("Hi") };
    print(Arrays.equals(s1, s2));
  }
} /* Output:
true
false
true
*///:~

16.7.3 数组元素的比较

排序必须根据对象的实际类型执行比较操作。
程序设计的基本目标是:将保存不变的事物与会发生改变的事物相分离。这里,不变的是通用的排序算法,变化的是各种对象相互比较的方式。通过使用策略设计模式,可以将会发生变化的代码封装在单独的类中(策略对象),你可以将策略对象传递给总是相同的代码。
一种比较实现Comparable接口:

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class CompType implements Comparable<CompType> {
  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<CompType> generator() {
    return new Generator<CompType>() {
      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());
    print("before sorting:");
    print(Arrays.toString(a));
    Arrays.sort(a);
    print("after sorting:");
    print(Arrays.toString(a));
  }
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40]
, [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89]
, [i = 58, j = 55], [i = 61, j = 29], [i = 68, j = 0]
, [i = 88, j = 28], [i = 93, j = 61], [i = 98, j = 61]
]
*///:~

另一种方法,Collection类包含一个reverseOrder()fangfa,该方法可以产生一个Comparator,它可以反转自然排序顺序:

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class Reverse {
  public static void main(String[] args) {
    CompType[] a = Generated.array(
      new CompType[12], CompType.generator());
    print("before sorting:");
    print(Arrays.toString(a));
    Arrays.sort(a, Collections.reverseOrder());
    print("after sorting:");
    print(Arrays.toString(a));
  }
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
, [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55]
, [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58]
, [i = 16, j = 40], [i = 11, j = 22], [i = 9, j = 78]
]
*///:~

也可以自己编写Comparator:

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class CompTypeComparator implements Comparator<CompType> {
  public int compare(CompType o1, CompType o2) {
    return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1));
  }
}
public class ComparatorTest {
  public static void main(String[] args) {
    CompType[] a = Generated.array(
      new CompType[12], CompType.generator());
    print("before sorting:");
    print(Arrays.toString(a));
    Arrays.sort(a, new CompTypeComparator());
    print("after sorting:");
    print(Arrays.toString(a));
  }
} /* Output:
before sorting:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
after sorting:
[[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22]
, [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40]
, [i = 58, j = 55], [i = 20, j = 58], [i = 93, j = 61]
, [i = 98, j = 61], [i = 9, j = 78], [i = 51, j = 89]
]
*///:~

16.7.4 数组排序

使用内置的排序方法,就可以对任意的基本类型数组排序,也可以对任意的对象数组进行排序,只要该对象实现了Comparable接口或具有关联的Comparator。
随机生成String对象,并排序:

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class StringSorting {
  public static void main(String[] args) {
    String[] sa = Generated.array(new String[20],
      new RandomGenerator.String(5));
    print("Before sort: " + Arrays.toString(sa));
    Arrays.sort(sa);
    print("After sort: " + Arrays.toString(sa));
    Arrays.sort(sa, Collections.reverseOrder());
    print("Reverse sort: " + Arrays.toString(sa));
    Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
    print("Case-insensitive sort: " + Arrays.toString(sa));
  }
}

排序算法针对正排序的特殊类型进行了优化——针对基本类型设计的"快速排序",以及针对对象设计的"稳定归并排序"。

16.7.5 在已排序的数组中查找

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
public class ArraySearching {
  public static void main(String[] args) {
    Generator<Integer> gen =
      new RandomGenerator.Integer(1000);
    int[] a = ConvertTo.primitive(
      Generated.array(new Integer[25], gen));
    Arrays.sort(a);
    print("Sorted array: " + Arrays.toString(a));
    while(true) {
      int r = gen.next();
      int location = Arrays.binarySearch(a, r);
      if(location >= 0) {
        print("Location of " + r + " is " + location +
          ", a[" + location + "] = " + a[location]);
        break; // Out of while loop
      }
    }
  }
} 

如果使用Comparator排序某个对象数组,在使用binarySearch()时必须提供同样的Comparator。

import java.util.*;
import net.mindview.util.*;
public class AlphabeticSearch {
  public static void main(String[] args) {
    String[] sa = Generated.array(new String[30],
      new RandomGenerator.String(5));
    Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
    System.out.println(Arrays.toString(sa));
    int index = Arrays.binarySearch(sa, sa[10],
      String.CASE_INSENSITIVE_ORDER);
    System.out.println("Index: "+ index + "\n"+ sa[index]);
  }
} /* Output:
[bkIna, cQrGs, cXZJo, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HqXum, HxxHv, JMRoE, JmzMs, Mesbt, MNvqe, nyGcF, ogoYW, OneOE, OWZnT, RFJQA, rUkZP, sgqia, slJrL, suEcU, uTpnX, vpfFv, WHkjU, xxEAJ, YNzbr, zDyCy]
Index: 10
HxxHv
*///:~

posted on 2019-08-02 14:33  Mr.Tan&  阅读(140)  评论(0编辑  收藏  举报

导航