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

16.1 数组为什么特殊

数组与其他种类的容器之间的区别有三方面:效率,类型和保存基本类型的能力。

数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常快速。但是为这种速度所付出的代价是数组对象的大小固定,并且在其生命周期中不可改变。

ArrayList:它可以通过一个新实例,然后把旧实例中所有引用移到新实例中,从而实现更多空间的自动分配。但它的效率比数组低很多。

数组可以持有基本类型,而泛型之前的容器不能。但有了泛型,容器旧可以指定并检查它们所持有对象的类型,并且有了自动包装机制,容器看起来还能够持有基本类型。

数组和泛型容器比较:

  1. import java.util.*;
  2. import static net.mindview.util.Print.*;
  3. class BerylliumSphere {
  4. private static long counter;
  5. private final long id = counter++;
  6. public String toString() { return "Sphere " + id; }
  7. }
  8. public class ContainerComparison {
  9. public static void main(String[] args) {
  10. BerylliumSphere[] spheres = new BerylliumSphere[10];
  11. for(int i = 0; i < 5; i++)
  12. spheres[i] = new BerylliumSphere();
  13. print(Arrays.toString(spheres));
  14. print(spheres[4]);
  15. List<BerylliumSphere> sphereList =
  16. new ArrayList<BerylliumSphere>();
  17. for(int i = 0; i < 5; i++)
  18. sphereList.add(new BerylliumSphere());
  19. print(sphereList);
  20. print(sphereList.get(4));
  21. int[] integers = { 0, 1, 2, 3, 4, 5 };
  22. print(Arrays.toString(integers));
  23. print(integers[4]);
  24. List<Integer> intList = new ArrayList<Integer>(
  25. Arrays.asList(0, 1, 2, 3, 4, 5));
  26. intList.add(97);
  27. print(intList);
  28. print(intList.get(4));
  29. }
  30. } /* Output:
  31. [Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4, null, null, null, null, null]
  32. Sphere 4
  33. [Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9]
  34. Sphere 9
  35. [0, 1, 2, 3, 4, 5]
  36. 4
  37. [0, 1, 2, 3, 4, 5, 97]
  38. 4
  39. *///:~

数组的优点是效率。

16.2 数组是第一级对象

无论使用哪种类型的数组,数组标识符其实只是一个引用,指向在堆中创建的一个真实对象,这个对象用以保存指向其他对象的引用。

对象数组和基本类型数组在使用上几乎是相同的。唯一区别就是对象数组保存的是引用,基本类型数组直接保存基本类型的值。

  1. import java.util.*;
  2. import static net.mindview.util.Print.*;
  3. public class ArrayOptions {
  4. public static void main(String[] args) {
  5. // Arrays of objects:
  6. BerylliumSphere[] a; // Local uninitialized variable
  7. BerylliumSphere[] b = new BerylliumSphere[5];
  8. // The references inside the array are
  9. // automatically initialized to null:
  10. print("b: " + Arrays.toString(b));
  11. BerylliumSphere[] c = new BerylliumSphere[4];
  12. for(int i = 0; i < c.length; i++)
  13. if(c[i] == null) // Can test for null reference
  14. c[i] = new BerylliumSphere();
  15. // Aggregate initialization:
  16. BerylliumSphere[] d = { new BerylliumSphere(),
  17. new BerylliumSphere(), new BerylliumSphere()
  18. };
  19. print("d"+Arrays.toString(d));
  20. // Dynamic aggregate initialization:
  21. a = new BerylliumSphere[]{
  22. new BerylliumSphere(), new BerylliumSphere(),
  23. };
  24. // (Trailing comma is optional in both cases)
  25. print("a.length = " + a.length);
  26. print("b.length = " + b.length);
  27. print("c.length = " + c.length);
  28. print("d.length = " + d.length);
  29. a = d;
  30. print("a.length = " + a.length);
  31. // Arrays of primitives:
  32. int[] e; // Null reference
  33. int[] f = new int[5];
  34. // The primitives inside the array are
  35. // automatically initialized to zero:
  36. print("f: " + Arrays.toString(f));
  37. int[] g = new int[4];
  38. for(int i = 0; i < g.length; i++)
  39. g[i] = i*i;
  40. int[] h = { 11, 47, 93 };
  41. // Compile error: variable e not initialized:
  42. //!print("e.length = " + e.length);
  43. print("f.length = " + f.length);
  44. print("g.length = " + g.length);
  45. print("h.length = " + h.length);
  46. e = h;
  47. print("e.length = " + e.length);
  48. e = new int[]{ 1, 2 };
  49. print("e.length = " + e.length);
  50. }
  51. } /* Output:
  52. b: [null, null, null, null, null]
  53. a.length = 2
  54. b.length = 5
  55. c.length = 4
  56. d.length = 3
  57. a.length = 3
  58. f: [0, 0, 0, 0, 0]
  59. f.length = 5
  60. g.length = 4
  61. h.length = 3
  62. e.length = 3
  63. e.length = 2
  64. *///:~

16.3 返回一个数组

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

  1. import java.util.*;
  2. public class IceCream {
  3. private static Random rand = new Random(47);
  4. static final String[] FLAVORS = {
  5. "Chocolate", "Strawberry", "Vanilla Fudge Swirl",
  6. "Mint Chip", "Mocha Almond Fudge", "Rum Raisin",
  7. "Praline Cream", "Mud Pie"
  8. };
  9. public static String[] flavorSet(int n) {
  10. if(n > FLAVORS.length)
  11. throw new IllegalArgumentException("Set too big");
  12. String[] results = new String[n];
  13. boolean[] picked = new boolean[FLAVORS.length];
  14. for(int i = 0; i < n; i++) {
  15. int t;
  16. do
  17. t = rand.nextInt(FLAVORS.length);
  18. while(picked[t]);
  19. results[i] = FLAVORS[t];
  20. picked[t] = true;
  21. }
  22. return results;
  23. }
  24. public static void main(String[] args) {
  25. for(int i = 0; i < 7; i++)
  26. System.out.println(Arrays.toString(flavorSet(3)));
  27. }
  28. } /* Output:
  29. [Rum Raisin, Mint Chip, Mocha Almond Fudge]
  30. [Chocolate, Strawberry, Mocha Almond Fudge]
  31. [Strawberry, Mint Chip, Mocha Almond Fudge]
  32. [Rum Raisin, Vanilla Fudge Swirl, Mud Pie]
  33. [Vanilla Fudge Swirl, Chocolate, Mocha Almond Fudge]
  34. [Praline Cream, Strawberry, Mocha Almond Fudge]
  35. [Mocha Almond Fudge, Strawberry, Mint Chip]
  36. *///:~

16.4 多维数组

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

  1. import java.util.*;
  2. public class MultidimensionalPrimitiveArray {
  3. public static void main(String[] args) {
  4. int[][] a = {
  5. { 1, 2, 3, },
  6. { 4, 5, 6, },
  7. };System.out.println(Arrays.deepToString(a));
  8. System.out.println(a[0].length);
  9. System.out.println(a[1].length);
  10. }
  11. } /* Output:
  12. [[1, 2, 3], [4, 5, 6]]
  13. *///:~

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

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

你可以看到基本类型数组得值在不进行显式初始化得情况下,会被自动初始化。对象数组会被初始化为null。

数组中构成矩阵得每个向量都可以具有任意得长度(粗糙数组)

  1. import java.util.*;
  2. public class RaggedArray {
  3. public static void main(String[] args) {
  4. Random rand = new Random(47);
  5. // 3-D array with varied-length vectors:
  6. int[][][] a = new int[rand.nextInt(7)][][];
  7. for(int i = 0; i < a.length; i++) {
  8. a[i] = new int[rand.nextInt(5)][];
  9. for(int j = 0; j < a[i].length; j++)
  10. a[i][j] = new int[rand.nextInt(5)];
  11. }
  12. System.out.println(Arrays.deepToString(a));
  13. }
  14. } /* Output:
  15. [[], [[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]]]
  16. *///:~

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

  1. import java.util.*;
  2. public class MultidimensionalObjectArrays {
  3. public static void main(String[] args) {
  4. BerylliumSphere[][] spheres = {
  5. { new BerylliumSphere(), new BerylliumSphere() },
  6. { new BerylliumSphere(), new BerylliumSphere(),
  7. new BerylliumSphere(), new BerylliumSphere() },
  8. { new BerylliumSphere(), new BerylliumSphere(),
  9. new BerylliumSphere(), new BerylliumSphere(),
  10. new BerylliumSphere(), new BerylliumSphere(),
  11. new BerylliumSphere(), new BerylliumSphere() },
  12. };
  13. System.out.println(Arrays.deepToString(spheres));
  14. }
  15. } /* Output:
  16. [[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]]
  17. *///:~

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

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

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

  1. import java.util.*;
  2. public class MultiDimWrapperArray {
  3. public static void main(String[] args) {
  4. Integer[][] a1 = { // Autoboxing
  5. { 1, 2, 3, },
  6. { 4, 5, 6, },
  7. };
  8. Double[][][] a2 = { // Autoboxing
  9. { { 1.1, 2.2 }, { 3.3, 4.4 } },
  10. { { 5.5, 6.6 }, { 7.7, 8.8 } },
  11. { { 9.9, 1.2 }, { 2.3, 3.4 } },
  12. };
  13. String[][] a3 = {
  14. { "The", "Quick", "Sly", "Fox" },
  15. { "Jumped", "Over" },
  16. { "The", "Lazy", "Brown", "Dog", "and", "friend" },
  17. };
  18. String[][] a4=new String[2][3];
  19. System.out.println("a1: " + Arrays.deepToString(a1));
  20. System.out.println("a2: " + Arrays.deepToString(a2));
  21. System.out.println("a3: " + Arrays.deepToString(a3));
  22. System.out.println("a3: " + Arrays.deepToString(a4));
  23. }
  24. } /* Output:
  25. a1: [[1, 2, 3], [4, 5, 6]]
  26. 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]]]
  27. a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The, Lazy, Brown, Dog, and, friend]]
  28. *///:~

16.5 数组与泛型

数组与泛型不能很好的结合。你不能实例化具有参数化类型的数组。

擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。

参数化数组本身的类型:

  1. class ClassParameter<T> {
  2. public T[] f(T[] arg) { return arg; }
  3. }
  4. class MethodParameter {
  5. public static <T> T[] f(T[] arg) { return arg; }
  6. }
  7. public class ParameterizedArrayType {
  8. public static void main(String[] args) {
  9. Integer[] ints = { 1, 2, 3, 4, 5 };
  10. Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 };
  11. Integer[] ints2 =
  12. new ClassParameter<Integer>().f(ints);
  13. Double[] doubles2 =
  14. new ClassParameter<Double>().f(doubles);
  15. ints2 = MethodParameter.f(ints);
  16. doubles2 = MethodParameter.f(doubles);
  17. }
  18. } ///:~

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

  1. import java.util.*;
  2. public class ArrayOfGenerics {
  3. @SuppressWarnings("unchecked")
  4. public static void main(String[] args) {
  5. List<String>[] ls;
  6. List[] la = new List[10];
  7. ls = (List<String>[])la; // "Unchecked" warning
  8. ls[0] = new ArrayList<String>();
  9. // Compile-time checking produces an error:
  10. //ls[1] = new ArrayList<Integer>();
  11. // The problem: List<String> is a subtype of Object
  12. Object[] objects = ls; // So assignment is OK
  13. // Compiles and runs without complaint:
  14. objects[1] = new ArrayList<Integer>();
  15. // However, if your needs are straightforward it is
  16. // possible to create an array of generics, albeit
  17. // with an "unchecked" warning:
  18. List<BerylliumSphere>[] spheres =
  19. (List<BerylliumSphere>[])new List[10];
  20. for(int i = 0; i < spheres.length; i++)
  21. spheres[i] = new ArrayList<BerylliumSphere>();
  22. }
  23. } ///:~

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

不能创建泛型数组:

  1. public class ArrayOfGenericType<T> {
  2. T[] array; // OK
  3. @SuppressWarnings("unchecked")
  4. public ArrayOfGenericType(int size) {
  5. //! array = new T[size]; // Illegal
  6. array = (T[])new Object[size]; // "unchecked" Warning
  7. }
  8. // Illegal:
  9. //! public <U> U[] makeArray() { return new U[10]; }
  10. } ///:~

16.6 创建测试数据

16.6.1 Arrays.fill()

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

  1. import java.util.*;
  2. import static net.mindview.util.Print.*;
  3. public class FillingArrays {
  4. public static void main(String[] args) {
  5. int size = 6;
  6. boolean[] a1 = new boolean[size];
  7. byte[] a2 = new byte[size];
  8. char[] a3 = new char[size];
  9. short[] a4 = new short[size];
  10. int[] a5 = new int[size];
  11. long[] a6 = new long[size];
  12. float[] a7 = new float[size];
  13. double[] a8 = new double[size];
  14. String[] a9 = new String[size];
  15. Arrays.fill(a1, true);
  16. print("a1 = " + Arrays.toString(a1));
  17. Arrays.fill(a2, (byte)11);
  18. print("a2 = " + Arrays.toString(a2));
  19. Arrays.fill(a3, 'x');
  20. print("a3 = " + Arrays.toString(a3));
  21. Arrays.fill(a4, (short)17);
  22. print("a4 = " + Arrays.toString(a4));
  23. Arrays.fill(a5, 19);
  24. print("a5 = " + Arrays.toString(a5));
  25. Arrays.fill(a6, 23);
  26. print("a6 = " + Arrays.toString(a6));
  27. Arrays.fill(a7, 29);
  28. print("a7 = " + Arrays.toString(a7));
  29. Arrays.fill(a8, 47);
  30. print("a8 = " + Arrays.toString(a8));
  31. Arrays.fill(a9, "Hello");
  32. print("a9 = " + Arrays.toString(a9));
  33. // Manipulating ranges:
  34. Arrays.fill(a9, 3, 5, "World");
  35. print("a9 = " + Arrays.toString(a9));
  36. }
  37. } /* Output:
  38. a1 = [true, true, true, true, true, true]
  39. a2 = [11, 11, 11, 11, 11, 11]
  40. a3 = [x, x, x, x, x, x]
  41. a4 = [17, 17, 17, 17, 17, 17]
  42. a5 = [19, 19, 19, 19, 19, 19]
  43. a6 = [23, 23, 23, 23, 23, 23]
  44. a7 = [29.0, 29.0, 29.0, 29.0, 29.0, 29.0]
  45. a8 = [47.0, 47.0, 47.0, 47.0, 47.0, 47.0]
  46. a9 = [Hello, Hello, Hello, Hello, Hello, Hello]
  47. a9 = [Hello, Hello, Hello, World, World, Hello]
  48. *///:~

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

16.6.2 数据生成器

  1. package net.mindview.util;
  2. public class CountingGenerator {
  3. static char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
  4. public CountingGenerator() {
  5. }
  6. public static class Boolean implements Generator<java.lang.Boolean> {
  7. private boolean value = false;
  8. public Boolean() {
  9. }
  10. public java.lang.Boolean next() {
  11. this.value = !this.value;
  12. return this.value;
  13. }
  14. }
  15. public static class Byte implements Generator<java.lang.Byte> {
  16. private byte value = 0;
  17. public Byte() {
  18. }
  19. public java.lang.Byte next() {
  20. return this.value++;
  21. }
  22. }
  23. public static class Character implements Generator<java.lang.Character> {
  24. int index = -1;
  25. public Character() {
  26. }
  27. public java.lang.Character next() {
  28. this.index = (this.index + 1) % CountingGenerator.chars.length;
  29. return CountingGenerator.chars[this.index];
  30. }
  31. }
  32. public static class Double implements Generator<java.lang.Double> {
  33. private double value = 0.0D;
  34. public Double() {
  35. }
  36. public java.lang.Double next() {
  37. double result = (double)(this.value++);
  38. return result;
  39. }
  40. }
  41. public static class Float implements Generator<java.lang.Float> {
  42. private float value = 0.0F;
  43. public Float() {
  44. }
  45. public java.lang.Float next() {
  46. float result = this.value;
  47. this.value = (float)((double)this.value + 1.0D);
  48. return result;
  49. }
  50. }
  51. public static class Integer implements Generator<java.lang.Integer> {
  52. private int value = 0;
  53. public Integer() {
  54. }
  55. public java.lang.Integer next() {
  56. return this.value++;
  57. }
  58. }
  59. public static class Long implements Generator<java.lang.Long> {
  60. private long value = 0L;
  61. public Long() {
  62. }
  63. public java.lang.Long next() {
  64. return java.lang.Long.valueOf((long)(this.value++));
  65. }
  66. }
  67. public static class Short implements Generator<java.lang.Short> {
  68. private short value = 0;
  69. public Short() {
  70. }
  71. public java.lang.Short next() {
  72. return this.value++;
  73. }
  74. }
  75. public static class String implements Generator<java.lang.String> {
  76. private int length = 7;
  77. Generator<java.lang.Character> cg = new CountingGenerator.Character();
  78. public String() {
  79. }
  80. public String(int length) {
  81. this.length = length;
  82. }
  83. public java.lang.String next() {
  84. char[] buf = new char[this.length];
  85. for(int i = 0; i < this.length; ++i) {
  86. buf[i] = ((java.lang.Character)this.cg.next()).charValue();
  87. }
  88. return new java.lang.String(buf);
  89. }
  90. }
  91. }

通过反射使用这个工具:

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

使用随机数生成器的Generator:

  1. package net.mindview.util;
  2. import java.util.Random;
  3. public class RandomGenerator {
  4. private static Random r = new Random(47L);
  5. public RandomGenerator() {
  6. }
  7. public static class Boolean implements Generator<java.lang.Boolean> {
  8. public Boolean() {
  9. }
  10. public java.lang.Boolean next() {
  11. return RandomGenerator.r.nextBoolean();
  12. }
  13. }
  14. public static class Byte implements Generator<java.lang.Byte> {
  15. public Byte() {
  16. }
  17. public java.lang.Byte next() {
  18. return (byte)RandomGenerator.r.nextInt();
  19. }
  20. }
  21. public static class Character implements Generator<java.lang.Character> {
  22. public Character() {
  23. }
  24. public java.lang.Character next() {
  25. return CountingGenerator.chars[RandomGenerator.r.nextInt(CountingGenerator.chars.length)];
  26. }
  27. }
  28. public static class Double implements Generator<java.lang.Double> {
  29. public Double() {
  30. }
  31. public java.lang.Double next() {
  32. long trimmed = Math.round(RandomGenerator.r.nextDouble() * 100.0D);
  33. return (double)trimmed / 100.0D;
  34. }
  35. }
  36. public static class Float implements Generator<java.lang.Float> {
  37. public Float() {
  38. }
  39. public java.lang.Float next() {
  40. int trimmed = Math.round(RandomGenerator.r.nextFloat() * 100.0F);
  41. return (float)trimmed / 100.0F;
  42. }
  43. }
  44. public static class Integer implements Generator<java.lang.Integer> {
  45. private int mod = 10000;
  46. public Integer() {
  47. }
  48. public Integer(int modulo) {
  49. this.mod = modulo;
  50. }
  51. public java.lang.Integer next() {
  52. return RandomGenerator.r.nextInt(this.mod);
  53. }
  54. }
  55. public static class Long implements Generator<java.lang.Long> {
  56. private int mod = 10000;
  57. public Long() {
  58. }
  59. public Long(int modulo) {
  60. this.mod = modulo;
  61. }
  62. public java.lang.Long next() {
  63. return new java.lang.Long((long)RandomGenerator.r.nextInt(this.mod));
  64. }
  65. }
  66. public static class Short implements Generator<java.lang.Short> {
  67. public Short() {
  68. }
  69. public java.lang.Short next() {
  70. return (short)RandomGenerator.r.nextInt();
  71. }
  72. }
  73. public static class String extends net.mindview.util.CountingGenerator.String {
  74. public String() {
  75. this.cg = new RandomGenerator.Character();
  76. }
  77. public String(int length) {
  78. super(length);
  79. this.cg = new RandomGenerator.Character();
  80. }
  81. }
  82. }

16.6.3 从Generator中创建数组

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

  1. package net.mindview.util;
  2. import java.lang.reflect.Array;
  3. public class Generated {
  4. public Generated() {
  5. }
  6. public static <T> T[] array(T[] a, Generator<T> gen) {
  7. return (new CollectionData(gen, a.length)).toArray(a);
  8. }
  9. public static <T> T[] array(Class<T> type, Generator<T> gen, int size) {
  10. Object[] a = (Object[])Array.newInstance(type, size);
  11. return (new CollectionData(gen, size)).toArray(a);
  12. }
  13. }

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

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

泛型不能用于基本类型,可以用生成器来填充基本类型。

创建一个转换器,它可以接受任意的包装器对象数组,并将其转化为相应的基本类型数组:

  1. package net.mindview.util;
  2. public class ConvertTo {
  3. public ConvertTo() {
  4. }
  5. public static boolean[] primitive(Boolean[] in) {
  6. boolean[] result = new boolean[in.length];
  7. for(int i = 0; i < in.length; ++i) {
  8. result[i] = in[i].booleanValue();
  9. }
  10. return result;
  11. }
  12. public static char[] primitive(Character[] in) {
  13. char[] result = new char[in.length];
  14. for(int i = 0; i < in.length; ++i) {
  15. result[i] = in[i].charValue();
  16. }
  17. return result;
  18. }
  19. public static byte[] primitive(Byte[] in) {
  20. byte[] result = new byte[in.length];
  21. for(int i = 0; i < in.length; ++i) {
  22. result[i] = in[i].byteValue();
  23. }
  24. return result;
  25. }
  26. public static short[] primitive(Short[] in) {
  27. short[] result = new short[in.length];
  28. for(int i = 0; i < in.length; ++i) {
  29. result[i] = in[i].shortValue();
  30. }
  31. return result;
  32. }
  33. public static int[] primitive(Integer[] in) {
  34. int[] result = new int[in.length];
  35. for(int i = 0; i < in.length; ++i) {
  36. result[i] = in[i].intValue();
  37. }
  38. return result;
  39. }
  40. public static long[] primitive(Long[] in) {
  41. long[] result = new long[in.length];
  42. for(int i = 0; i < in.length; ++i) {
  43. result[i] = in[i].longValue();
  44. }
  45. return result;
  46. }
  47. public static float[] primitive(Float[] in) {
  48. float[] result = new float[in.length];
  49. for(int i = 0; i < in.length; ++i) {
  50. result[i] = in[i].floatValue();
  51. }
  52. return result;
  53. }
  54. public static double[] primitive(Double[] in) {
  55. double[] result = new double[in.length];
  56. for(int i = 0; i < in.length; ++i) {
  57. result[i] = in[i].doubleValue();
  58. }
  59. return result;
  60. }
  61. }

16.7 Arrays实用功能

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

16.7.1 复制数组

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

  1. import java.util.*;
  2. import static net.mindview.util.Print.*;
  3. public class CopyingArrays {
  4. public static void main(String[] args) {
  5. int[] i = new int[7];
  6. int[] j = new int[10];
  7. Arrays.fill(i, 47);
  8. Arrays.fill(j, 99);
  9. print("i = " + Arrays.toString(i));
  10. print("j = " + Arrays.toString(j));
  11. System.arraycopy(i, 0, j, 0, i.length);
  12. print("j = " + Arrays.toString(j));
  13. int[] k = new int[5];
  14. Arrays.fill(k, 103);
  15. System.arraycopy(i, 0, k, 0, k.length);
  16. print("k = " + Arrays.toString(k));
  17. Arrays.fill(k, 103);
  18. System.arraycopy(k, 0, i, 0, k.length);
  19. print("i = " + Arrays.toString(i));
  20. // Objects:
  21. Integer[] u = new Integer[10];
  22. Integer[] v = new Integer[5];
  23. Arrays.fill(u, new Integer(47));
  24. Arrays.fill(v, new Integer(99));
  25. print("u = " + Arrays.toString(u));
  26. print("v = " + Arrays.toString(v));
  27. System.arraycopy(v, 0, u, u.length/2, v.length);
  28. print("u = " + Arrays.toString(u));
  29. }
  30. } /* Output:
  31. i = [47, 47, 47, 47, 47, 47, 47]
  32. j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
  33. j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
  34. k = [47, 47, 47, 47, 47]
  35. i = [103, 103, 103, 103, 103, 47, 47]
  36. u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47]
  37. v = [99, 99, 99, 99, 99]
  38. u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
  39. *///:~

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

16.7.2 数组的比较

  1. import java.util.*;
  2. import static net.mindview.util.Print.*;
  3. public class ComparingArrays {
  4. public static void main(String[] args) {
  5. int[] a1 = new int[10];
  6. int[] a2 = new int[10];
  7. Arrays.fill(a1, 47);
  8. Arrays.fill(a2, 47);
  9. print(Arrays.equals(a1, a2));
  10. a2[3] = 11;
  11. print(Arrays.equals(a1, a2));
  12. String[] s1 = new String[4];
  13. Arrays.fill(s1, "Hi");
  14. String[] s2 = { new String("Hi"), new String("Hi"),
  15. new String("Hi"), new String("Hi") };
  16. print(Arrays.equals(s1, s2));
  17. }
  18. } /* Output:
  19. true
  20. false
  21. true
  22. *///:~

16.7.3 数组元素的比较

排序必须根据对象的实际类型执行比较操作。

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

一种比较实现Comparable接口:

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. import static net.mindview.util.Print.*;
  4. public class CompType implements Comparable<CompType> {
  5. int i;
  6. int j;
  7. private static int count = 1;
  8. public CompType(int n1, int n2) {
  9. i = n1;
  10. j = n2;
  11. }
  12. public String toString() {
  13. String result = "[i = " + i + ", j = " + j + "]";
  14. if(count++ % 3 == 0)
  15. result += "\n";
  16. return result;
  17. }
  18. public int compareTo(CompType rv) {
  19. return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
  20. }
  21. private static Random r = new Random(47);
  22. public static Generator<CompType> generator() {
  23. return new Generator<CompType>() {
  24. public CompType next() {
  25. return new CompType(r.nextInt(100),r.nextInt(100));
  26. }
  27. };
  28. }
  29. public static void main(String[] args) {
  30. CompType[] a =
  31. Generated.array(new CompType[12], generator());
  32. print("before sorting:");
  33. print(Arrays.toString(a));
  34. Arrays.sort(a);
  35. print("after sorting:");
  36. print(Arrays.toString(a));
  37. }
  38. } /* Output:
  39. before sorting:
  40. [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
  41. , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
  42. , [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
  43. , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
  44. ]
  45. after sorting:
  46. [[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40]
  47. , [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89]
  48. , [i = 58, j = 55], [i = 61, j = 29], [i = 68, j = 0]
  49. , [i = 88, j = 28], [i = 93, j = 61], [i = 98, j = 61]
  50. ]
  51. *///:~

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

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. import static net.mindview.util.Print.*;
  4. public class Reverse {
  5. public static void main(String[] args) {
  6. CompType[] a = Generated.array(
  7. new CompType[12], CompType.generator());
  8. print("before sorting:");
  9. print(Arrays.toString(a));
  10. Arrays.sort(a, Collections.reverseOrder());
  11. print("after sorting:");
  12. print(Arrays.toString(a));
  13. }
  14. } /* Output:
  15. before sorting:
  16. [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
  17. , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
  18. , [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
  19. , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
  20. ]
  21. after sorting:
  22. [[i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
  23. , [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55]
  24. , [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58]
  25. , [i = 16, j = 40], [i = 11, j = 22], [i = 9, j = 78]
  26. ]
  27. *///:~

也可以自己编写Comparator:

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. import static net.mindview.util.Print.*;
  4. class CompTypeComparator implements Comparator<CompType> {
  5. public int compare(CompType o1, CompType o2) {
  6. return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1));
  7. }
  8. }
  9. public class ComparatorTest {
  10. public static void main(String[] args) {
  11. CompType[] a = Generated.array(
  12. new CompType[12], CompType.generator());
  13. print("before sorting:");
  14. print(Arrays.toString(a));
  15. Arrays.sort(a, new CompTypeComparator());
  16. print("after sorting:");
  17. print(Arrays.toString(a));
  18. }
  19. } /* Output:
  20. before sorting:
  21. [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
  22. , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
  23. , [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
  24. , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
  25. ]
  26. after sorting:
  27. [[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22]
  28. , [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40]
  29. , [i = 58, j = 55], [i = 20, j = 58], [i = 93, j = 61]
  30. , [i = 98, j = 61], [i = 9, j = 78], [i = 51, j = 89]
  31. ]
  32. *///:~

16.7.4 数组排序

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

随机生成String对象,并排序:

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. import static net.mindview.util.Print.*;
  4. public class StringSorting {
  5. public static void main(String[] args) {
  6. String[] sa = Generated.array(new String[20],
  7. new RandomGenerator.String(5));
  8. print("Before sort: " + Arrays.toString(sa));
  9. Arrays.sort(sa);
  10. print("After sort: " + Arrays.toString(sa));
  11. Arrays.sort(sa, Collections.reverseOrder());
  12. print("Reverse sort: " + Arrays.toString(sa));
  13. Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
  14. print("Case-insensitive sort: " + Arrays.toString(sa));
  15. }
  16. }

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

16.7.5 在已排序的数组中查找

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. import static net.mindview.util.Print.*;
  4. public class ArraySearching {
  5. public static void main(String[] args) {
  6. Generator<Integer> gen =
  7. new RandomGenerator.Integer(1000);
  8. int[] a = ConvertTo.primitive(
  9. Generated.array(new Integer[25], gen));
  10. Arrays.sort(a);
  11. print("Sorted array: " + Arrays.toString(a));
  12. while(true) {
  13. int r = gen.next();
  14. int location = Arrays.binarySearch(a, r);
  15. if(location >= 0) {
  16. print("Location of " + r + " is " + location +
  17. ", a[" + location + "] = " + a[location]);
  18. break; // Out of while loop
  19. }
  20. }
  21. }
  22. }

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

  1. import java.util.*;
  2. import net.mindview.util.*;
  3. public class AlphabeticSearch {
  4. public static void main(String[] args) {
  5. String[] sa = Generated.array(new String[30],
  6. new RandomGenerator.String(5));
  7. Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
  8. System.out.println(Arrays.toString(sa));
  9. int index = Arrays.binarySearch(sa, sa[10],
  10. String.CASE_INSENSITIVE_ORDER);
  11. System.out.println("Index: "+ index + "\n"+ sa[index]);
  12. }
  13. } /* Output:
  14. [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]
  15. Index: 10
  16. HxxHv
  17. *///:~

Java基础之十六 数组的更多相关文章

  1. 集合框架、泛型、迭代(java基础知识十六)

    1.ArrayList存储自定义对象并遍历 此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法 ...

  2. 夯实Java基础(十六)——枚举类的使用

    1.枚举类简介 枚举是仅容许特定数据类型值的有限集合.例如我们平时生活中星期一到星期日这七天就是一个特定的有限集合,一年四季的春夏秋冬也同样是的,它们都是枚举.枚举和我们数学中的集合非常相似,如果我们 ...

  3. java基础(十六)集合(三)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

  4. Java基础(十六)断言(Assertions)

    1.断言的概念 假设确信某个属性符合要求,并且代码的执行依赖于这个属性. 断言机制允许在测试期间向代码插入一些检查语句,当代码发布时,这些插入的检查语句将会被自动地移走. 断言失败是致命的,不可恢复的 ...

  5. java基础第十六篇之多线程

    1:线程的概念 进程(任务):一个正在运行的程序 进程的调度:CPU来决定什么时候该运行哪个进程 (时间片轮流法) 线程在一个应用程序中,同时,有多个不同的执行路径,是进程中的实际运作单位. 好处是提 ...

  6. java基础解析系列(六)---深入注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...

  7. java基础解析系列(六)---注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...

  8. “全栈2019”Java多线程第二十六章:同步方法生产者与消费者线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. “全栈2019”Java多线程第十六章:同步synchronized关键字详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

随机推荐

  1. Python 3 的 int 类型详解(为什么 int 不存在溢出问题?)

    在以前的Python2中,整型分为int和long,也就是整型和长整型, 长整型不存在溢出问题, 即可以存放任意大小的数值,理论支持无限大数字. 因此在Python3 中,统一使用长整型,用int表示 ...

  2. docker安装mysql8

    docker run --restart=always -d -v /opt/data/conf.d/:/etc/mysql/conf.d/ -v /opt/data/mysql/:/var/lib/ ...

  3. SQL Server使用sp_executesql在存储过程中执行多个批处理

    SQL Server中有些SQL语句只能在一个批处理里面完成,例如CREATE SCHEMA语句创建SCHEMA的时候,每个SCHEMA都需要在一个单独的批处理里面完成: CREATE SCHEMA ...

  4. 关于“关于C#装箱的疑问”帖子的个人看法 (原发布csdn 2017年10月07日 10:21:10)

    前言 昨天晚上闲着无事,就上csdn逛了一下,突然发现一个帖子很有意思,就点进去看了一下. 问题很精辟 int a = 1; object b=a; object c = b; c = 2; 为什么b ...

  5. C++初探

    //string1.cpp #include <iostream> int main() { using namespace std; ]={'d','o','g'}//这是char数组, ...

  6. c#WinForm中TeeChart控件的注册和使用

    首先要注册好TeeChart控件,注册方法参考:https://blog.csdn.net/my_clear_mind/article/details/79741020 完成注册之后,新建一个WinF ...

  7. SpringBoot项目解决全局响应返回中文乱码问题

    一.问题 新建的基于SpringBoot的MVC项目,在请响应体中,如果有中文,会显示为乱码. 二.解决方案 1.在application.properties中设置: spring.http.enc ...

  8. Qt keyevent学习笔记

    在按下一个键不放后,会发生: 1.触发keypressevent(),此时isautorepeat()返回false: 2.set isautorepeat(),使其返回值为true; 3.触发key ...

  9. python数据分析三剑客之: matplotlib绘图模块

    matplotlib 一.Matplotlib基础知识 Matplotlib中的基本图表包括的元素 - x轴和y轴 axis 水平和垂直的轴线 - x轴和y轴刻度 tick 刻度标示坐标轴的分隔,包括 ...

  10. 使用Dictionary键值对判断字符串中字符出现次数

    介绍Dictionary 使用前需引入命名空间 using System.Collections.Generic Dictionary里面每一个元素都是一个键值对(由两个元素组成:键和值) 键必须是唯 ...