thinking-in-java(16) 数组

 【16.1】数组有什么特殊
 1)数组与其他类型容器的区别:
效率,类型和保持基本类型的能力;
 数组是效率最高的存储和随机访问对象引用序列的方式;
 数组大小固定,容器大小可以不固定,所以这增加了弹性开销,使得ArrayList的效率比数组低很多;
 
 【荔枝-数组与泛型容器的比较】 
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();
    // Arrays.toString() 打印数组各个元素的toString()
    print("Arrays.toString(spheres) = " + Arrays.toString(spheres)); 
    print("spheres[4] = " + spheres[4]);


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


    int[] integers = { 0, 1, 2, 3, 4, 5 };
    print("Arrays.toString(integers) = " + Arrays.toString(integers));
    print("integers[4] = " + integers[4]);
    
    // 用一个List A存放另外一个List B的元素(另外一个List B的大小是不可变的,但一个List A, 即intList的大小是可变的 )
    List<Integer> intList = new ArrayList<Integer>(Arrays.asList(0, 1, 2, 3, 4, 5));
    intList.add(97);
    print("intList = " + intList);
    print("intList.get(4) = " + intList.get(4));
  }
}  
/*
Arrays.toString(spheres) = [Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4, null, null, null, null, null]
spheres[4] = Sphere 4
sphereList = [Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9]
sphereList.get(4) = Sphere 9
Arrays.toString(integers) = [0, 1, 2, 3, 4, 5]
integers[4] = 4
intList = [0, 1, 2, 3, 4, 5, 97]
intList.get(4) = 4
*/
2)随着自动包装机制的出现,容器可以和数组一样存储基本数据类型;数组的优点仅仅是效率;

【16.2】数组是第一级对象
1)只读成员length:
数组对象的一部分,表示数组对象可以存储多少元素(length 是数组唯一可访问的字段);
2)[]语法:是访问数组对象唯一的方式;
3)唯一区别:
对象数组保存的是引用,基本类型数组保存基本类型的值;


【荔枝-初始化数组的方式】

// 对象数组和基本类型数组的比较荔枝
public class ArrayOptions {
  public static void main(String[] args) {
    // 对象数组
    BerylliumSphere[] a; // 本地未初始化
    BerylliumSphere[] b = new BerylliumSphere[5];
    // 数组中的引用自动初始化为 null
    print("BerylliumSphere[] b = new BerylliumSphere[5]; Arrays.toString(b) = " + Arrays.toString(b));
    
    BerylliumSphere[] c = new BerylliumSphere[4];
    for(int i = 0; i < c.length; i++) // length 是数组唯一可访问的字段
      if(c[i] == null) 
        c[i] = new BerylliumSphere();
   
    // 聚集初始化(在两种情况下,尾随逗号都是可选的)
    BerylliumSphere[] d = { new BerylliumSphere(),
      new BerylliumSphere(), new BerylliumSphere(),
    };
    // 动态聚集初始化(在两种情况下,尾随逗号都是可选的)
    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 = d; a.length = " + a.length);


    // 基本类型数组
    int[] e; // 空引用 
    //!print("e.length = " + e.length); // 编译报错:参数e 未初始化
    
    int[] f = new int[5];
    // 基本类型数组中的基本类型值自动初始化为 0 
    print("int[] f = new int[5]; Arrays.toString(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 };
    print("g.length = " + g.length);
    print("int[] h = { 11, 47, 93 }; h.length = " + h.length);
    e = h;
    print("e = h; e.length = " + e.length);
    e = new int[]{ 1, 2 };
    print("e = new int[]{ 1, 2 }; e.length = " + e.length);
  }
} 
/*
BerylliumSphere[] b = new BerylliumSphere[5]; Arrays.toString(b) = [null, null, null, null, null]
a.length = 2
b.length = 5
c.length = 4
d.length = 3
a = d; a.length = 3
int[] f = new int[5]; Arrays.toString(f) = [0, 0, 0, 0, 0]
g.length = 4
int[] h = { 11, 47, 93 }; h.length = 3
e = h; e.length = 3
e = new int[]{ 1, 2 }; e.length = 2
*/

4)数组的小缺点: 无法知道数组中确切有多少元素,因为length是数组的大小,并不是实际保存的元素个数;
5)数组初始化策略: 初始化引用类型为null,基本类型数值型为0,字符型为 '\0'(转换为int类型时,值等于0);布尔型为false;
6)聚集初始化: 隐式地使用 new 在堆中创建,就像数组一样;
7)聚集初始化和动态聚集初始化的区别: 聚集初始化必须在定义数组的位置调用,而动态聚集初始化可以在任意位置创建和初始化对象;

【16.3】返回一个数组
【荔枝-直接返回一个数组】

// 方法返回一个数组的荔枝(C+ 和 C++的方法不能返回一个数组而返回一个指针)
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]; // 默认false
   
    for(int i = 0; i < n; i++) {
      int t;
      do {
        t = rand.nextInt(FLAVORS.length); // 产生小于 FLAVORS.length 的随机数
      } while(picked[t]);
      results[i] = FLAVORS[t]; 
      picked[t] = true;
      // while(picked[t]) 和 picked[t] = true; 的写法非常经典 以确保不会重复选择!!
    }
    
    return results; // 返回一个 String[] 数组
  }
  
  public static void main(String[] args) {
    for(int i = 0; i < 7; i++)
      System.out.println(Arrays.toString(flavorSet(3)));
  }
} 
/*
[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】多维数组
1)创建多维数组:
对于基本类型的多维数组,可以通过使用 花括号 把每个向量分隔开;


【荔枝-创建多维基本类型数组】

// 荔枝-创建多维基本类型数组
public class MultidimensionalPrimitiveArray {
  public static void main(String[] args) {
	// 基本类型的多维数组 
    int[][] a = { 
      { 1, 2, 3, },
      { 4, 5, 6, },
    };
    // Arrays.deepToString(a), Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(a));
  }
}
// [[1, 2, 3], [4, 5, 6]]


【荔枝-使用 new关键字创建数组】
// 【荔枝-使用 new关键字创建数组】
public class ThreeDWithNew {
  public static void main(String[] args) {
	// 采用new 关键字创建 长度固定的3维基本类型数组, 默认为0 
    int[][][] a = new int[2][2][4];
    // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(a));  
  }
} 
// [[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]] 

【注意-Arrays.toString(array)是把一维数组转换为多个String;Arrays.deepToString()把多维数组转换为多个String】


2)Arrays.toString(array)与Arrays.deepToString(array)区别:
区别1:
toString的输入参数既可以是 基本数据类型也可以是引用类型;而deepToString的输入参数只能是引用类型(Object[]);
区别2:toString 仅能够把一维数组转换为多个String,不能把2维度或以上维度的数组转换为多个String,对于大于等于2维以上的数组,toString只能返回其内存地址的hashcode;
相反deepToString 可以转换所有维度的数组为多个String,非常棒;


【荔枝-使用 new关键字创建数组(Arrays.toString(array)与Arrays.deepToString(array)区别)】

public class ThreeDWithNew {
  public static void main(String[] args) {
	// 采用new 关键字创建 长度固定的3维基本类型数组, 默认为0 
    Integer[][][] a = new Integer[2][2][4];
    
    // 1.转换3维数组:
    // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println("Arrays.deepToString(a)) = " + Arrays.deepToString(a));
    // 注意Arrays.deepToString() 和 Arrays.toString()方法的区别
    System.out.println("Arrays.deepToString(a)) = " + Arrays.toString(a));
    
    // 2.转换2维数组:
    System.out.println("Arrays.deepToString(a[0]) = " + Arrays.deepToString(a[0]));
    System.out.println("Arrays.toString(a[0]) = " + Arrays.toString(a[0]));
    
    // 3.转换1维数组:
    System.out.println("Arrays.deepToString(a)) = " + Arrays.deepToString(a[0][0]));
    System.out.println("Arrays.deepToString(a)) = " + Arrays.toString(a[0][0]));
  }
} 
/*
Arrays.deepToString(a)) = [[[null, null, null, null], [null, null, null, null]], [[null, null, null, null], [null, null, null, null]]]
Arrays.deepToString(a)) = [[[Ljava.lang.Integer;@6d06d69c, [[Ljava.lang.Integer;@70dea4e]
Arrays.deepToString(a[0]) = [[null, null, null, null], [null, null, null, null]]
Arrays.toString(a[0]) = [[Ljava.lang.Integer;@7852e922, [Ljava.lang.Integer;@4e25154f]
Arrays.deepToString(a)) = [null, null, null, null]
Arrays.deepToString(a)) = [null, null, null, null]
*/ 

3)粗糙数组或不规则数组:每个数组向量的长度不相同;


【荔枝-创建粗糙数组或不规则数组】

// 创建基本类型的粗糙数组,即每个数组向量的长度不相同;(也叫不规整数组)
public class RaggedArray {
  public static void main(String[] args) {
    Random rand = new Random(47);
    // 边长向量的3维
    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)]; // 这里的数组长度是随机的 
    }
    // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(a));
  }
} 
// [[], [[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]]]

【荔枝-创建引用类型的粗糙数组】
// 创建引用类型的粗糙数组
public class MultidimensionalObjectArrays {
	
  public static void main(String[] args) {
    BerylliumSphere[][] spheres = {
      { new BerylliumSphere(), new BerylliumSphere() }, // 长度为2
      { new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere() }, // 长度为4
      { new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere(),
        new BerylliumSphere(), new BerylliumSphere() }, // 长度为8
    };
    // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(spheres));
  }
}
/*
[[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]]
*/ 
【荔枝- 数组元素自动装箱】

// 数组元素自动装箱的荔枝(基本数据类型 转换为 Integer 引用类型)
public class AutoboxingArrays {
  public static void main(String[] args) {
	// 自动装箱: int 基本数据类型 转换为 Integer 引用类型
	// 基本类型数组 转换为 引用类型数组 或 对象数组
    Integer[][] a = { 
      { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
      { 21, 22, 23, 24, 25, 26, 27, 28, 29},
      { 51, 52, 53, 54, 55, 56, 57, 58},
      { 71, 72, 73, 74, 75, 76, 77, }, // 最后的逗号 可加 也可以不加
    };
 // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(a));
  }
}
/*
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 
[21, 22, 23, 24, 25, 26, 27, 28, 29], 
[51, 52, 53, 54, 55, 56, 57, 58], 
[71, 72, 73, 74, 75, 76, 77]]
*/

【荔枝-运用自动装箱机制对非基本类型的对象数组进行赋值】

// 运用自动装箱机制对非基本类型的对象数组进行赋值的荔枝
public class AssemblingMultidimensionalArrays {
  public static void main(String[] args) {
    Integer[][] a;
    a = new Integer[3][];
    
    for(int i = 0; i < a.length; i++) {
      a[i] = new Integer[3];
      for(int j = 0; j < a[i].length; j++)
        a[i][j] = i * j; // 自动装箱机制
    }
 // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    System.out.println(Arrays.deepToString(a));
  }
}  
/*
[[0, 0, 0], [0, 1, 2], [0, 2, 4]]
*/

【荔枝-Arrays.deepToString() 只能接受Object[] 类型数组,而不是接受基本类型,但基本类型的包装器类型是可以的;】

// Arrays.deepToString() 方法对基本类型数组和对象数组都起作用;
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" },
    };
    // Arrays.deepToString(Array array) 可以把多维数组转换为多个 String;
    
    System.out.println("a1 = " + Arrays.deepToString(a1));
    System.out.println("a2 = " + Arrays.deepToString(a2));
    System.out.println("a3 = " + Arrays.deepToString(a3));
  }
} 
/* Arrays.deepToString() 源码: 只能接受Object[] 类型数组,而不是接受基本类型,但基本类型的包装器类型是可以的;
 public static String deepToString(Object[] a) {
        if (a == null)
            return "null";


        int bufLen = 20 * a.length;
        if (a.length != 0 && bufLen <= 0)
            bufLen = Integer.MAX_VALUE;
        StringBuilder buf = new StringBuilder(bufLen);
        deepToString(a, buf, new HashSet<Object[]>());
        return buf.toString();
    }
*/
/*
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】数组与泛型
1)数组和泛型无法很好结合:
无法实例化具有参数化类型的数组;
Animal<Yaan>[] animals = new Animal<Yaan>[10]; // 这是非法的;


2)泛型擦除:会移除参数类型参数,而数组必须知道存储元素的确切类型,这就矛盾了;

3)可以参数化数组本身的类型:


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);
  }
} 

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

4)如何间接创建泛型数组:先创建非泛型数组,然后将其转换为泛型数组;

【荔枝-间接实现泛型数组的荔枝(非常经典)】

// 间接实现泛型数组的荔枝-创建非泛型数组,然后将其转型 为泛型容器类型
public class ArrayOfGenerics {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    List<String>[] ls;
    List[] la = new List[10];
    ls = (List<String>[])la; // 强制类型转换,
    ls[0] = new ArrayList<String>(Arrays.asList("str1, str2, str3"));
    // 编译时检查 报错:
    //! ls[1] = new ArrayList<Integer>(); // 这个的泛型是Integer,而ls被赋值为 String泛型,肯定报错啊;


    // The problem: List<String> is a subtype of Object
    // List<String> 是 Object 的子类型 ,所以赋值是可以的;
    Object[] objects = ls; 
    // 编译和运行无误
    objects[1] = new ArrayList<Integer>(Arrays.asList(new Integer[]{1, 2, 3})); // 注意:这里居然编译和运行通过;
    for (int i = 0; i < objects.length; i++) {
		System.out.println("objects[" + i +"] = " + objects[i]);
	}
    
    // 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>();
  }
} 
/*
objects[0] = [str1, str2, str3]
objects[1] = [1, 2, 3]
objects[2] = null
objects[3] = null
objects[4] = null
objects[5] = null
objects[6] = null
objects[7] = null
objects[8] = null
objects[9] = null
*/
【荔枝-如何创建泛型数组(先创建Object[]数组,然后将其转型为泛型数组)】

// 荔枝-如何创建泛型数组(先创建Object[] 数组,然后将其转型为泛型数组)
public class ArrayOfGenericType<T> {
  T[] array; // OK
  
  @SuppressWarnings("unchecked") // 阻止弹出 "不受检查" 的警告信息
  public ArrayOfGenericType(int size) {
    //! array = new T[size]; // 非法, 不能创建泛型数组;
	  
	// 阻止弹出 "不受检查" 的警告信息  
	// 但是可以创建非泛型数组Object[],然后通过强制类型转换,转换为泛型数组;(间接创建泛型数组)
    array = (T[])new Object[size]; 
  }
  //非法, 不能创建泛型数组;
  //! public <U> U[] makeArray() { return new U[10]; }
} 

【16.6】创建测试数据:使用对象或数值来填充数组

【16.6.1】Arrays.fill() 方法

// 荔枝-Arrays.fill() 填充数组
public class FillingArrays {
  public static void main(String[] args) {
    int size = 6;
    boolean[] a1 = new boolean[size]; // boolean 布尔数组
    byte[] a2 = new byte[size]; // byte 字节数组 
    char[] a3 = new char[size]; // char 字符数组
    short[] a4 = new short[size]; // short 短整型数组
    int[] a5 = new int[size]; // int 整型数组 
    long[] a6 = new long[size]; // long 长整型数组
    float[] a7 = new float[size]; // float 浮点型数组
    double[] a8 = new double[size]; // double 双浮点型数组
    String[] a9 = new String[size]; // String 字符串数组
    
    Arrays.fill(a1, true); // 填充boolean 布尔数组
    print("Arrays.fill(a1, true); a1 = " + Arrays.toString(a1));
    
    Arrays.fill(a2, (byte)11); // 填充 byte 字节数组 
    print("Arrays.fill(a2, (byte)11); a2 = " + Arrays.toString(a2));
    
    Arrays.fill(a3, 'x'); // 填充 char 字符数组
    print("Arrays.fill(a3, 'x'); a3 = " + Arrays.toString(a3));
    
    Arrays.fill(a4, (short)17);  // 填充 short 短整型数组
    print("Arrays.fill(a4, (short)17); a4 = " + Arrays.toString(a4));
    
    Arrays.fill(a5, 19); // 填充 int 整型数组 
    print("Arrays.fill(a5, 19); a5 = " + Arrays.toString(a5));
    
    Arrays.fill(a6, 23); // 填充 long 长整型数组
    print("Arrays.fill(a6, 23); a6 = " + Arrays.toString(a6));
    
    Arrays.fill(a7, 29); // 填充 float 浮点型数组
    print("Arrays.fill(a7, 29); a7 = " + Arrays.toString(a7));
    
    Arrays.fill(a8, 47); // 填充 double 双浮点型数组
    print("Arrays.fill(a8, 47); a8 = " + Arrays.toString(a8));
    
    Arrays.fill(a9, "Hello"); // 填充 String 字符串数组
    print("Arrays.fill(a9, \"Hello\"); a9 = " + Arrays.toString(a9));
    
    // 这里是填充数组的某个区域
    Arrays.fill(a9, 3, 5, "World"); // 填充 String 字符串数组(下标3 到下标5,不包括下标5,从0开始计数)
    print("Arrays.fill(a9, 3, 5, \"World\"); a9 = " + Arrays.toString(a9));
  }
}
/*
Arrays.fill(a1, true); a1 = [true, true, true, true, true, true]
Arrays.fill(a2, (byte)11); a2 = [11, 11, 11, 11, 11, 11]
Arrays.fill(a3, 'x'); a3 = [x, x, x, x, x, x]
Arrays.fill(a4, (short)17); a4 = [17, 17, 17, 17, 17, 17]
Arrays.fill(a5, 19); a5 = [19, 19, 19, 19, 19, 19]
Arrays.fill(a6, 23); a6 = [23, 23, 23, 23, 23, 23]
Arrays.fill(a7, 29); a7 = [29.0, 29.0, 29.0, 29.0, 29.0, 29.0]
Arrays.fill(a8, 47); a8 = [47.0, 47.0, 47.0, 47.0, 47.0, 47.0]
Arrays.fill(a9, "Hello"); a9 = [Hello, Hello, Hello, Hello, Hello, Hello]
Arrays.fill(a9, 3, 5, "World"); a9 = [Hello, Hello, Hello, World, World, Hello]
*/

【16.6.2】数据生成器
1)策略模式来填充数组:
通过选择Generator的类型来创建任何类型的数据,每个不同Generator都表示不同的策略;


【荔枝-通过静态内部类实现数据生成器】

// 通过静态内部类实现 数据生成器
public class CountingGenerator {
	// 通过静态内部类实现
	public static class Boolean implements Generator<java.lang.Boolean> {
		private boolean value = false;


		public java.lang.Boolean next() {
			value = !value; // Just flips back and forth
			return value;
		}
	}


	public static class Byte implements Generator<java.lang.Byte> {
		private byte value = 0;


		public java.lang.Byte next() {
			return value++;
		}
	}


	static char[] chars = ("abcdefghijklmnopqrstuvwxyz"
			+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();


	public static class Character implements Generator<java.lang.Character> {
		int index = -1;


		public java.lang.Character next() {
			index = (index + 1) % chars.length;
			return chars[index];
		}
	}


	public static class String implements Generator<java.lang.String> {
		private int length = 7;
		Generator<java.lang.Character> cg = new Character();


		public String() {
		}


		public String(int length) {
			this.length = length;
		}


		public java.lang.String next() {
			char[] buf = new char[length];
			for (int i = 0; i < length; i++)
				buf[i] = cg.next(); // 通过字符生成字符串
			return new java.lang.String(buf);
		}
	}


	public static class Short implements Generator<java.lang.Short> {
		private short value = 0;


		public java.lang.Short next() {
			return value++;
		}
	}


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


		public java.lang.Integer next() {
			return value++;
		}
	}


	public static class Long implements Generator<java.lang.Long> {
		private long value = 0;


		public java.lang.Long next() {
			return value++;
		}
	}


	public static class Float implements Generator<java.lang.Float> {
		private float value = 0;


		public java.lang.Float next() {
			float result = value;
			value += 1.0;
			return result;
		}
	}


	public static class Double implements Generator<java.lang.Double> {
		private double value = 0.0;


		public java.lang.Double next() {
			double result = value;
			value += 1.0;
			return result;
		}
	}
}
【数据生成器的测试荔枝-通过策略模式来填充数组】 
// 数据生成器的测试荔枝-策略模式来填充数组,其中利用反射机制来获取 Class类实例数组
public class GeneratorsTest {
  public static int size = 10;
  
  public static void test(Class<?> surroundingClass) {
	  // surroundingClass.getClasses() 获取 Class类对应的内部类的Class类实例数组
    for(Class<?> type : surroundingClass.getClasses()) {
      System.out.print(type.getSimpleName() + ": ");
      try {
    	// 传入Class类信息,利用反射机制创建数据生成器
        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
*/
【荔枝-利用Random 创建随机数据生成器的荔枝】

// 利用Random 创建随机数据生成器的荔枝
public class RandomGenerator {
  private static Random r = new Random(47);
  
  public static class Boolean implements Generator<java.lang.Boolean> {
    public java.lang.Boolean next() {
      return r.nextBoolean();
    }
  }
  
  public static class Byte implements Generator<java.lang.Byte> {
    public java.lang.Byte next() {
      return (byte)r.nextInt(); // 调用的是 Random.nextInt()
    }
  }
  
  public static class Character implements Generator<java.lang.Character> {
    public java.lang.Character next() {
    	// // 调用的是 Random.nextInt()
      return CountingGenerator.chars[r.nextInt(CountingGenerator.chars.length)];
    }
  }
  
  // 该 RandomGenerator.String 继承自 CountingGenerator.String 类 (内部类继承机制)
  public static class String extends CountingGenerator.String {
    // Plug in the random Character generator:
    { cg = new Character(); } // Instance initializer
    public String() {}
    public String(int length) { super(length); }
  }
  
  public static class Short implements Generator<java.lang.Short> {
    public java.lang.Short next() {
      return (short)r.nextInt(); // 调用的是 Random.nextInt()
    }
  }
  
  public static class Integer implements Generator<java.lang.Integer> {
    private int mod = 10000;
    public Integer() {}
    public Integer(int modulo) { mod = modulo; }
    public java.lang.Integer next() {
      return r.nextInt(mod); // 调用的是 Random.nextInt()
    }
  }
  
  public static class Long implements Generator<java.lang.Long> {
    private int mod = 10000;
    public Long() {}
    public Long(int modulo) { mod = modulo; }
    public java.lang.Long next() {
      return new java.lang.Long(r.nextInt(mod)); // 调用的是 Random.nextInt()
    }
  }
  
  public static class Float implements Generator<java.lang.Float> {
    public java.lang.Float next() {
      // Trim all but the first two decimal places:
      int trimmed = Math.round(r.nextFloat() * 100); // 调用的是 Random.nextFloat()
      return ((float)trimmed) / 100;
    }
  }
  
  public static class Double implements Generator<java.lang.Double> {
    public java.lang.Double next() {
      long trimmed = Math.round(r.nextDouble() * 100); // 调用的是 Random.nextFloat()
      return ((double)trimmed) / 100;
    }
  }
} 
/* Random.next*() 方法列表有:
protected int next(int bits) {
public void nextBytes(byte[] bytes) {
final long internalNextLong(long origin, long bound) {
 
 public int nextInt() {
    return next(32); // next(bits=32)
}
    
public long nextLong() {
public boolean nextBoolean() {
public float nextFloat() {
public double nextDouble() {
*/

【随机数据生成器的测试荔枝-】

// 测试荔枝-利用随机数据生成器填充数组
public class RandomGeneratorsTest {
  public static void main(String[] args) {
    GeneratorsTest.test(RandomGenerator.class);
  }
} 
/* 
Double: 0.73 0.53 0.16 0.19 0.52 0.27 0.26 0.05 0.8 0.76
Float: 0.53 0.16 0.53 0.4 0.49 0.25 0.8 0.11 0.02 0.8
Long: 7674 8804 8950 7826 4322 896 8033 2984 2344 5810
Integer: 8303 3141 7138 6012 9966 8689 7185 6992 5746 3976
Short: 3358 20592 284 26791 12834 -8092 13656 29324 -1423 5327
String: bkInaMe sbtWHkj UrUkZPg wsqPzDy CyRFJQA HxxHvHq XumcXZJ oogoYWM NvqeuTp nXsgqia
Character: x x E A J J m z M s
Byte: -60 -17 55 -14 -5 115 39 -37 79 115
Boolean: false true false false true true true true true true
*/


【16.6.3】从Generator中创建数组

1)为了接受Generator 作为输入参数并产生数组,需要两个转换工具:
工具1)填充已有数组:
接受一个已有数组a,并使用Generator 来填充该数组(填充已有数组)
工具2)创建新数组并填充:接受一个 Class对象 和 数组大小 来创建一个新数组,并用 Generator来填充该数组(创建新数组并填充)

// 荔枝-从Generator中创建数组
public class Generated {
  // 方法1:接受一个已有数组a,并使用Generator 来填充该数组(填充已有数组)
  public static <T> T[] array(T[] a, Generator<T> gen) {
    return new CollectionData<T>(gen, a.length).toArray(a);
  }
  @SuppressWarnings("unchecked")
  // 方法2:接受一个 Class对象 和 数组大小 来创建一个新数组,并用 Generator来填充该数组(创建新数组并填充)
  public static <T> T[] array(Class<T> type, Generator<T> gen, int size) {
    // 利用反射创建对象数组,全部赋值为 null
	T[] a = (T[])java.lang.reflect.Array.newInstance(type, size);
    for ( T t : a ) {
    	System.out.println("t = " + t);
    }
    return new CollectionData<T>(gen, size).toArray(a);
  }
  
  public static void main(String[] args) {
	  Integer[] array = array(Integer.class, new RandomGenerator.Integer(), 5);
	  System.out.println("after filling by generator, Arrays.toString(array) = " + Arrays.toString(array));
  }
} 
/*
t = null
t = null
t = null
t = null
t = null
after filling by generator, Arrays.toString(array) = [9258, 555, 6693, 1861, 961]
*/

【测试荔枝-从Generator中创建数组的两个转换工具】
// 测试荔枝-从Generator中创建数组的两个转换工具
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("a = " + Arrays.toString(a));  
    
    Integer[] b = Generated.array(Integer.class, new CountingGenerator.Integer(), 15);
    System.out.println("b = " + Arrays.toString(b));  
  }
} 
/*
[9, 8, 7, 6]
a = [0, 1, 2, 3]
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
t = null
b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
*/


2)数组类型转换器:该转换器可以接收任意的包装器对象数组,并将其转换为相应的基本类型数组;

【荔枝-为基本类型创建生成器】

// 荔枝-为基本类型创建生成器
public class ConvertTo {
  // Boolean 类型转基本类型 boolean
  public static boolean[] primitive(Boolean[] in) {
    boolean[] result = new boolean[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  // Character 类型转基本类型 char
  public static char[] primitive(Character[] in) {
    char[] result = new char[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  //Byte 类型转基本类型 byte
  public static byte[] primitive(Byte[] in) {
    byte[] result = new byte[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  //Short 类型转基本类型 short
  public static short[] primitive(Short[] in) {
    short[] result = new short[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  //Integer 类型转基本类型 int
  public static int[] primitive(Integer[] in) {
    int[] result = new int[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  //Long 类型转基本类型 long
  public static long[] primitive(Long[] in) {
    long[] result = new long[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  // Float 类型转基本类型 float
  public static float[] primitive(Float[] in) {
    float[] result = new float[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
  //Double 类型转基本类型 double
  public static double[] primitive(Double[] in) {
    double[] result = new double[in.length];
    for(int i = 0; i < in.length; i++)
      result[i] = in[i]; // 自动拆箱
    return result;
  }
} 
【ConvertTo()的测试荔枝1-如何把 ConvertTo(为基本类型创建生成器) 应用于 array(Class<T> type, Generator<T> gen, int size)  方法上】

// 荔枝-如何把 ConvertTo(为基本类型创建生成器) 应用于 array(Class<T> type, Generator<T> gen, int size)  方法上
public class PrimitiveConversionDemonstration {
	
  public static void main(String[] args) {
    Integer[] a = Generated.array(Integer.class, new CountingGenerator.Integer(), 15);
    int[] b = ConvertTo.primitive(a);
    System.out.println("int[] b = ConvertTo.primitive(a); b = " + Arrays.toString(b));
    
    boolean[] c = ConvertTo.primitive(Generated.array(Boolean.class, new CountingGenerator.Boolean(), 7));
    System.out.println("boolean[] c = ConvertTo.primitive(Generated.array(Boolean.class, new CountingGenerator.Boolean(), 7)); \n c = " 
    		+ Arrays.toString(c));
  }
} 
/* 
int[] b = ConvertTo.primitive(a); b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
boolean[] c = ConvertTo.primitive(Generated.array(Boolean.class, new CountingGenerator.Boolean(), 7)); 
 c = [true, false, true, false, true, false, true]
*/ 

【ConvertTo()的测试荔枝2-如何把 ConvertTo(为基本类型创建生成器) 应用于 array(Class<T> type, Generator<T> gen, int size) 方法上】

//荔枝-如何把 ConvertTo(为基本类型创建生成器) 应用于 array(Class<T> type, Generator<T> gen, int size) 方法上
public class TestArrayGeneration {
  public static void main(String[] args) {
    int size = 6;
    
    boolean[] a1 = ConvertTo.primitive(Generated.array(Boolean.class, new RandomGenerator.Boolean(), size));
    print("a1 = " + Arrays.toString(a1));
    
    byte[] a2 = ConvertTo.primitive(Generated.array(Byte.class, new RandomGenerator.Byte(), size));
    print("a2 = " + Arrays.toString(a2));
    
    char[] a3 = ConvertTo.primitive(Generated.array(Character.class, new RandomGenerator.Character(), size));
    print("a3 = " + Arrays.toString(a3));
    
    short[] a4 = ConvertTo.primitive(Generated.array(Short.class, new RandomGenerator.Short(), size));
    print("a4 = " + Arrays.toString(a4));
    
    int[] a5 = ConvertTo.primitive(Generated.array(Integer.class, new RandomGenerator.Integer(), size));
    print("a5 = " + Arrays.toString(a5));
    
    long[] a6 = ConvertTo.primitive(Generated.array(Long.class, new RandomGenerator.Long(), size));
    print("a6 = " + Arrays.toString(a6));
    
    float[] a7 = ConvertTo.primitive(Generated.array(Float.class, new RandomGenerator.Float(), size));
    print("a7 = " + Arrays.toString(a7));
    
    double[] a8 = ConvertTo.primitive(Generated.array(Double.class, new RandomGenerator.Double(), size));
    print("a8 = " + Arrays.toString(a8));
  }
} 
/*
a1 = [true, false, true, false, false, true]
a2 = [104, -79, -76, 126, 33, -64]
a3 = [Z, n, T, c, Q, r]
a4 = [-13408, 22612, 15401, 15161, -28466, -12603]
a5 = [7704, 7383, 7706, 575, 8410, 6342]
a6 = [7674, 8804, 8950, 7826, 4322, 896]
a7 = [0.01, 0.2, 0.4, 0.79, 0.27, 0.45]
a8 = [0.16, 0.87, 0.7, 0.66, 0.87, 0.59]
*/ 


【16.7】Arrays实用功能

1)7个Arrays的基本方法:
equals():用于比较两个数组是否相等;
deepEquals():用于比较两个多维数组是否相等;
fill():填充数组;
sort():对数组排序;
binarySearch():在有序的数组中查找元素;
toString():产生数组的String表示;
hashCode():产生数组的 hash code 散列码;

以上方法都对各种基本类型和Object类重载过;


Arrays.asList():接受任意序列或数组作为其参数,并将其转变为 List 容器;

【16.7.1】复制数组(System.arraycopy())
1)System.arraycopy():用于复制数组,该方法对所有类型做了重载;


【荔枝-使用arraycopy() 复制数组】

// 荔枝-使用 System.arraycopy() 进行数组拷贝
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("Arrays.fill(i, 47); i = " + Arrays.toString(i)); // i = [47, 47, 47, 47, 47, 47, 47]
    print("Arrays.fill(j, 99); j = " + Arrays.toString(j)); // j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
    
    // System.arraycopy(i, 0, j, 0, i.length); 复制数组i从0到i.length(不包括)的元素 到 数组j(j从位置0开始存储)
    System.arraycopy(i, 0, j, 0, i.length);
    // j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
    print("System.arraycopy(i, 0, j, 0, i.length); j = " + Arrays.toString(j)); 
    
    int[] k = new int[5];
    Arrays.fill(k, 103);
    System.arraycopy(i, 0, k, 0, k.length);
    // k = [47, 47, 47, 47, 47]
    print("Arrays.fill(k, 103); System.arraycopy(i, 0, k, 0, k.length); k = " + Arrays.toString(k));
    
    Arrays.fill(k, 103);
    System.arraycopy(k, 0, i, 0, k.length); // 复制数组k[0,length-1] 到 数组i[0,length-]
    // i = [103, 103, 103, 103, 103, 47, 47]
    print("Arrays.fill(k, 103); System.arraycopy(k, 0, i, 0, k.length); i = " + Arrays.toString(i));
    
    // 对象数组
    Integer[] u = new Integer[10];
    Integer[] v = new Integer[5];
    
    Arrays.fill(u, new Integer(47));
    Arrays.fill(v, new Integer(99));
    // u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47]
    print("Arrays.fill(u, new Integer(47)); u = " + Arrays.toString(u));
    // v = [99, 99, 99, 99, 99]
    print("Arrays.fill(v, new Integer(99)); v = " + Arrays.toString(v));
    
    System.arraycopy(v, 0, u, u.length/2, v.length);
    // u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
    print("System.arraycopy(v, 0, u, u.length/2, v.length); u = " + Arrays.toString(u));
  }
} 
/* 
Arrays.fill(i, 47); i = [47, 47, 47, 47, 47, 47, 47]
Arrays.fill(j, 99); j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
System.arraycopy(i, 0, j, 0, i.length); j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
Arrays.fill(k, 103); System.arraycopy(i, 0, k, 0, k.length); k = [47, 47, 47, 47, 47]
Arrays.fill(k, 103); System.arraycopy(k, 0, i, 0, k.length); i = [103, 103, 103, 103, 103, 47, 47]
Arrays.fill(u, new Integer(47)); u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47]
Arrays.fill(v, new Integer(99)); v = [99, 99, 99, 99, 99]
System.arraycopy(v, 0, u, u.length/2, v.length); u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
*/ 
【注意】arraycopy()方法不会执行自动包装和自动拆包,两个数组必须具有相同的确切类型;

【16.7.2】数组比较(equals()方法)
1)数组相等的条件:
数组元素个数必须相等,数组对应位置的元素也相等;
2)对于基本类型:需要使用基本类型的包装器类的 equals()方法,如对int类型使用 Integer.equals()做比较;


【荔枝-数组比较】

// 荔枝-数组比较
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)); // true: a1 与 a2 相等
    
    a2[3] = 11; // 修改数组a2的某个元素
    print(Arrays.equals(a1, a2)); // false: 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)); // true: s1 与 s2 相等
    // 其实从 Arrays.equals(s1, s2) = true,可以看出 Arrays.equals() 比较的是 值是否相等,而不是引用是否相等
  }
} 
/* Arrays.equals(Object a, Object a2) 源码
public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;


    int length = a.length;
    if (a2.length != length)
        return false;


    for (int i=0; i<length; i++) {
        Object o1 = a[i];
        Object o2 = a2[i];
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }


    return true;
}
*/
【16.7.3】数组元素比较
1)使用策略模式进行比较:
把会发生变化的代码封装在单独的类中(比较策略),然后把策略模式对象传递给总是相同的代码,然后这些代码再使用策略 模式对象来完成其比较;
2)java有两种方式提供比较功能:
方式1)
实现 java.lang.Comparable 接口,实现类有比较能力;
方式2)创建一个Comparator接口的实现类 来实现比较;

【方式1 荔枝-实现Comparable接口来比较】
// 使用策略模式,将策略对象传递给相同代码,这些代码使用策略来完成算法。
// 荔枝-java的两种比较方式,一是实现 Comparable接口
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;
  }
  // 这里的比较策略只用到了 属性i,没有属性j
  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("Arrays.toString(a) = " + Arrays.toString(a));
    
    Arrays.sort(a);
    print("Arrays.sort(a); Arrays.toString(a) = " + Arrays.toString(a));
  }
} 
/* Arrays.sort() 源码
public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a);
    else
        ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
*/
/* 打印结果
Arrays.toString(a) = [[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]
]
Arrays.sort(a); Arrays.toString(a) = [[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]
]
*/
【注意】如果没有实现 Comparable 接口,调用sort()方法的时候会抛出 ClassCastException 运行时异常。因为sort()方法需要把参数类型转变为 Comparable;
/* java.lang.Comparable接口源码
public interface Comparable<T> {
	public int compareTo(T o);
}
*/
3)Collections类包含一个 reverseOrder()方法,产生一个 Comparator实现类,以反转自然的排序顺序;


【方式2-荔枝-以反转自然的排序顺序对数组排序】

// 荔枝-以反转自然的排序顺序对数组排序
public class Reverse {
  public static void main(String[] args) {
    CompType[] a = Generated.array(new CompType[12], CompType.generator());
    
    print("Arrays.toString(a) = " + Arrays.toString(a));
    
    // CompType类的排序规则是 按照属性 i 升序排序的。
    Arrays.sort(a); // 第 1 次排序
    print("Arrays.sort(a); Arrays.toString(a) = " + Arrays.toString(a));
    
    // Collections.reverseOrder() 反转自然的排序顺序( 按照属性 i 降序排序的。)
    Arrays.sort(a, Collections.reverseOrder()); // 第 2 次排序 
    print("Arrays.sort(a, Collections.reverseOrder()); Arrays.toString(a) = " + Arrays.toString(a));
  }
} 
/* Arrays.sort(T[] a, Comparator<? super T> c) 源码
 public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);
    }
}
*/
/* 打印结果
// 1.原始的数组是乱序的
Arrays.toString(a) = [[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]
]
// 2.按照i 升序排序
Arrays.sort(a); Arrays.toString(a) = [[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]
]
// 3.按照i 降序排序
Arrays.sort(a, Collections.reverseOrder()); Arrays.toString(a) = [[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]
]
*/
/* Collections.reverseOrder() 源码:
 @SuppressWarnings("unchecked")
public static <T> Comparator<T> reverseOrder() {
	return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}
ReverseComparator 源码
private static class ReverseComparator
	implements Comparator<Comparable<Object>>, Serializable {


	private static final long serialVersionUID = 7207038068494060240L;


	static final ReverseComparator REVERSE_ORDER
		= new ReverseComparator();


	public int compare(Comparable<Object> c1, Comparable<Object> c2) {
		return c2.compareTo(c1);
	}


	private Object readResolve() { return Collections.reverseOrder(); }


	@Override
	public Comparator<Comparable<Object>> reversed() {
		return Comparator.naturalOrder();
	}
}
*/
/*
*/

【方式2-荔枝-自定义 Comparator 对数组排序(通过实现 Comparator接口来实现)】
// 自定义排序规则的荔枝(通过实现 Comparator接口来实现)
class CompTypeComparator implements Comparator<CompType> {
	// 排序规则: 按照	j 进行升序排序
  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("Arrays.toString(a) = " + Arrays.toString(a));
    
    Arrays.sort(a, new CompTypeComparator()); // 通过实现 Comparator接口来自定义排序规则对数组排序
    print("Arrays.sort(a, new CompTypeComparator()); Arrays.toString(a) = " + Arrays.toString(a));
  }
} 
/* 
// 1.数组乱序
Arrays.toString(a) = [[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]
]
//  2.按照	j 进行升序排序
Arrays.sort(a, new CompTypeComparator()); Arrays.toString(a) = [[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]
]
*/
【总结】Comparable 与 Comparator的区别:
区别1)重写的方法不同:

实现 Comparable接口:需要重写 public int compareTo(T o) 方法;需要比较的对象作为 Comparable实现类;
实现 Comparator接口:需要重写  int compare(T o1, T o2) 方法;需要比较的对象 并不作为 Comparator实现类,而是其他的比较策略对象作为 Comparator实现类;

区别2)实现方式不同:

通过实现 Comparable 接口这种方式实现数组比较的特点: 将需要比较的对象 与 比较方法 的实现代码 揉在一起 ;

通过实现 Comparator 接口这种方式实现数组比较的优点: 将需要比较的对象 与 比较方法 的实现代码 分离开;
区别3)最大的区别:基本类型数组无法使用 Comparator 进行排序;


【16.7.4】数组排序
1)数组排序的前提条件:
该对象实现了 Comparable 接口 或 有相关联的 Comparator ;


【荔枝-基于多种排序策略对数组排序】

// 荔枝-基于多种排序策略对数组排序
public class StringSorting {
  public static void main(String[] args) {
    String[] sa = Generated.array(new String[20], new RandomGenerator.String(5));
    print("\nArrays.toString(sa) = " + Arrays.toString(sa));
    Arrays.sort(sa); // 自然排序
    print("\nArrays.sort(sa); Arrays.toString(sa) = " + Arrays.toString(sa));
    
    Arrays.sort(sa, Collections.reverseOrder()); // 逆序
    print("\nArrays.sort(sa, Collections.reverseOrder());  Arrays.toString(sa) = " + Arrays.toString(sa));
    
    Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); // 忽略大小写敏感
    print("\nArrays.sort(sa, String.CASE_INSENSITIVE_ORDER); Arrays.toString(sa) = " + Arrays.toString(sa));
  }
}
/*
//1.原始数组
Arrays.toString(sa) = [YNzbr, nyGcF, OWZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE, dLsmw, HLGEa, hKcxr, EqUCB, bkIna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy, RFJQA, HxxHv]
//2.自然排序后的数组 
Arrays.sort(sa); Arrays.toString(sa) = [EqUCB, HLGEa, HxxHv, JMRoE, Mesbt, OWZnT, OneOE, RFJQA, WHkjU, YNzbr, bkIna, cQrGs, dLsmw, eGZMm, gwsqP, hKcxr, nyGcF, rUkZP, suEcU, zDyCy]
//3.自然逆序排序后的数组
Arrays.sort(sa, Collections.reverseOrder());  Arrays.toString(sa) = [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP, eGZMm, dLsmw, cQrGs, bkIna, YNzbr, WHkjU, RFJQA, OneOE, OWZnT, Mesbt, JMRoE, HxxHv, HLGEa, EqUCB]
//4.大小写不敏感的自然排序后的数组
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); Arrays.toString(sa) = [bkIna, cQrGs, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HxxHv, JMRoE, Mesbt, nyGcF, OneOE, OWZnT, RFJQA, rUkZP, suEcU, WHkjU, YNzbr, zDyCy]
*/
【注意】 String 排序算法依据 词典编排顺序排序,所以大写字母开头的词都排在前面,小写字母的词排在后面;(因为大写字母的ASCII码 小于 小写字母的 ASCII码)
【干货】java标准类库中的排序算法对正排序的特殊类型进行了优化:针对基本类型设计的快速排序 或 针对对象设计的 稳定归并排序。不需要担心java对数组的排序性能;

【16.7.5】在已排序的数组中查找(Arrays.binarySearch() 二分查找)
【荔枝-Arrays.binarySearch() 对有序数组进行二分查找】

// 荔枝-Arrays.binarySearch() 对有序数组进行二分查找
public class ArraySearching {
  public static void main(String[] args) {
    Generator<Integer> gen = new RandomGenerator.Integer(1000);
    // 利用随机数据生成器 产生Integer包装器数组并 转换为 int基本类型数组
    int[] a = ConvertTo.primitive(Generated.array(new Integer[25], gen));
    
    Arrays.sort(a); // 自然排序
    print("Arrays.sort(a); Arrays.toString(a) = " + Arrays.toString(a));
    
    while(true) {
      int r = gen.next();
      // Arrays.binarySearch(a, r); 在有序数组a中二分查找 r
      int location = Arrays.binarySearch(a, r); 
      
      if(location >= 0) {
        print("Location of " + r + " is " + location +
          ", a[" + location + "] = " + a[location]);
        break; 
      }
    }
  }
} 
/*
Sorted array: [128, 140, 200, 207, 258, 258, 278, 288, 322, 429, 511, 520, 522, 551, 555, 589, 693, 704, 809, 861, 861, 868, 916, 961, 998]
Location of 322 is 8, a[8] = 322
*/

1)Arrays.binarySearch(): 如果找到了目标,Arrays.binarySearch()产生的返回值等于或大于0;否则,返回值为负值,表示若要保持数组有序,该目标元素应该插入的位置;
2)上述负值计算方式: - (插入点) - 1;插入点指的是,第一个大于查找对象的元素在数组中的位置,如果数组中的所有元素都小于目标元素,则插入点就是 a.size();

3)如果使用 Comparator 排序了某个对象数组(基本类型数组无法使用 Comparator 进行排序),在使用 binarySearch() 时也必须提供同样的 Comparator;

// 荔枝-为二分查找 Arrays.binarySearch 设置排序规则
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.sort(sa, String.CASE_INSENSITIVE_ORDER); \nArrays.toString(sa) = " + Arrays.toString(sa));
    
    System.out.println("sa[10] = " + sa[10]);
    // 通过二分查找 查找出 sa[10] 元素的位置,其中排序规则同 Arrays.sort(sa) 的排序规则 String.CASE_INSENSITIVE_ORDER,否则报错
    int index = Arrays.binarySearch(sa, sa[10], String.CASE_INSENSITIVE_ORDER);
    System.out.println("Index: "+ index + "\n"+ sa[index]);
  }
}  
/*
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); 
Arrays.toString(sa) = [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
*/


【16.8】总结

1)使用容器来代替数组: 有了额外的自动包装机制和泛型,在容器中持有基本类型就很容易了。这也促使使用 容器而不是数组。因为泛型可以产生类型安全的容器;

2)优先容器而不是数组:只有在已证明性能称为问题,并且切换到数组对性能有提高时,你才应该将程序重构为使用数组;



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值