数组

在现在的stream和collections下,数组似乎没有什么优势了,但是不可否认它存在的必要。

数组的几种创建方式

// arrays/ArrayOptions.java
// Initialization & re-assignment of arrays
import java.util.*;
import static onjava.ArrayShow.*;

public class ArrayOptions {
  public static void main(String[] args) {
    // Arrays of objects:
    BerylliumSphere[] a; // Uninitialized local
    BerylliumSphere[] b = new BerylliumSphere[5];

    // The references inside the array are
    // automatically initialized to null:
    show("b", 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()
    };

    // Dynamic aggregate initialization:
    a = new BerylliumSphere[]{
      new BerylliumSphere(), new BerylliumSphere(),
    };
    // (Trailing comma is optional)

    System.out.println("a.length = " + a.length);
    System.out.println("b.length = " + b.length);
    System.out.println("c.length = " + c.length);
    System.out.println("d.length = " + d.length);
    a = d;
    System.out.println("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:
    show("f", 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:
    //- System.out.println("e.length = " + e.length);
    System.out.println("f.length = " + f.length);
    System.out.println("g.length = " + g.length);
    System.out.println("h.length = " + h.length);
    e = h;
    System.out.println("e.length = " + e.length);
    e = new int[]{ 1, 2 };
    System.out.println("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
*/

唯一的不同之处就是对象数组存储的是对象的引用,而基元数组则直接存储基本数据类型的值。

 

// arrays/RaggedArray.java
import java.util.*;

public class RaggedArray {
  static int val = 1;
  public static void main(String[] args) {
    SplittableRandom rand = new SplittableRandom(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)];
        Arrays.setAll(a[i][j], n -> val++); // [1]
      }
    }
    System.out.println(Arrays.deepToString(a));
  }
}
/* Output:
[[[1], []], [[2, 3, 4, 5], [6]], [[7, 8, 9], [10, 11,
12], []]]
*/

倘若你不对基元数组进行显式的初始化,它的值会自动初始化。而对象数组将被初始化为 null 。

 

泛型数组

一般来说,数组和泛型并不能很好的结合。你不能实例化参数化类型的数组:

Peel<Banana>[] peels = new Peel<Banana>[10]; // Illegal

泛型有类型擦除,而数组必须强制的知道保存的类型,所以不能初始化。但是可以泛型化数组本身。

// arrays/ParameterizedArrayType.java

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

 

Arrays.fill()

// arrays/FillingArrays.java
// Using Arrays.fill()
import java.util.*;
import static onjava.ArrayShow.*;

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);
    show("a1", a1);
    Arrays.fill(a2, (byte)11);
    show("a2", a2);
    Arrays.fill(a3, 'x');
    show("a3", a3);
    Arrays.fill(a4, (short)17);
    show("a4", a4);
    Arrays.fill(a5, 19);
    show("a5", a5);
    Arrays.fill(a6, 23);
    show("a6", a6);
    Arrays.fill(a7, 29);
    show("a7", a7);
    Arrays.fill(a8, 47);
    show("a8", a8);
    Arrays.fill(a9, "Hello");
    show("a9", a9);
    // Manipulating ranges:
    Arrays.fill(a9, 3, 5, "World");
    show("a9", a9);
  }
}gedan
/* 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]
*/

我们要杜绝用遍历来对数组对象添加同一个值,fill方法会在底层走指令级操作。

 

Arrays.setAll()

// arrays/SimpleSetAll.java

import java.util.*;
import static onjava.ArrayShow.*;

class Bob {
  final int id;
  Bob(int n) { id = n; }
  @Override
  public String toString() { return "Bob" + id; }
}

public class SimpleSetAll {
  public static final int SZ = 8;
  static int val = 1;
  static char[] chars = "abcdefghijklmnopqrstuvwxyz"
    .toCharArray();
  static char getChar(int n) { return chars[n]; }
  public static void main(String[] args) {
    int[] ia = new int[SZ];
    long[] la = new long[SZ];
    double[] da = new double[SZ];
    Arrays.setAll(ia, n -> n); // [1]
    Arrays.setAll(la, n -> n);
    Arrays.setAll(da, n -> n);
    show(ia);
    show(la);
    show(da);
    Arrays.setAll(ia, n -> val++); // [2]
    Arrays.setAll(la, n -> val++);
    Arrays.setAll(da, n -> val++);
    show(ia);
    show(la);
    show(da);

    Bob[] ba = new Bob[SZ];
    Arrays.setAll(ba, Bob::new); // [3]
    show(ba);

    Character[] ca = new Character[SZ];
    Arrays.setAll(ca, SimpleSetAll::getChar); // [4]
    show(ca);
  }
}
/* Output:
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
[1, 2, 3, 4, 5, 6, 7, 8]
[9, 10, 11, 12, 13, 14, 15, 16]
[17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0]
[Bob0, Bob1, Bob2, Bob3, Bob4, Bob5, Bob6, Bob7]
[a, b, c, d, e, f, g, h]
*/

 

并行化

流 实际上可以存储到将近1000万,但是之后就会耗尽堆空间。常规的 setAll() 是有效的,但是如果我们能更快地处理如此大量的数字,那就更好了。 我们可以使用 setAll() 初始化更大的数组。如果速度成为一个问题,Arrays.parallelSetAll() 将(可能)更快地执行初始化(请记住并行性中描述的问题)。

// arrays/ParallelSetAll.java

import onjava.*;
import java.util.Arrays;

public class ParallelSetAll {
    static final int SIZE = 10_000_000;

    static void intArray() {
        int[] ia = new int[SIZE];
        Arrays.setAll(ia, new Rand.Pint()::get);
        Arrays.parallelSetAll(ia, new Rand.Pint()::get);
    }

    static void longArray() {
        long[] la = new long[SIZE];
        Arrays.setAll(la, new Rand.Plong()::get);
        Arrays.parallelSetAll(la, new Rand.Plong()::get);
    }

    public static void main(String[] args) {
        intArray();
        longArray();
    }
}

 

Arrays工具类

  • asList(): 获取任何序列或数组,并将其转换为一个 列表集合 (集合章节介绍了此方法)。

  • copyOf():以新的长度创建现有数组的新副本。

  • copyOfRange():创建现有数组的一部分的新副本。

  • equals():比较两个数组是否相等。

  • deepEquals():多维数组的相等性比较。

  • stream():生成数组元素的流。

  • hashCode():生成数组的哈希值(您将在附录中了解这意味着什么:理解equals()和hashCode())。

  • deepHashCode(): 多维数组的哈希值。

  • sort():排序数组

  • parallelSort():对数组进行并行排序,以提高速度。

  • binarySearch():在已排序的数组中查找元素。

  • parallelPrefix():使用提供的函数并行累积(以获得速度)。基本上,就是数组的reduce()。

  • spliterator():从数组中产生一个Spliterator;这是本书没有涉及到的流的高级部分。

  • toString():为数组生成一个字符串表示。你在整个章节中经常看到这种用法。

  • deepToString():为多维数组生成一个字符串。你在整个章节中经常看到这种用法。对于所有基本类型和对象,所有这些方法都是重载的。

 

流和数组

// arrays/StreamFromArray.java

import java.util.*;
import onjava.*;

public class StreamFromArray {
    public static void main(String[] args) {
        String[] s = new Rand.String().array(10);
        Arrays.stream(s).skip(3).limit(5).map(ss -> ss + "!").forEach(System.out::println);
        int[] ia = new Rand.Pint().array(10);
        Arrays.stream(ia).skip(3).limit(5)
                .map(i -> i * 10).forEach(System.out::println);
        Arrays.stream(new long[10]);
        Arrays.stream(new double[10]);
        // Only int, long and double work:
        // - Arrays.stream(new boolean[10]);
        // - Arrays.stream(new byte[10]);
        // - Arrays.stream(new char[10]);
        // - Arrays.stream(new short[10]);
        // - Arrays.stream(new float[10]);
        // For the other types you must use wrapped arrays:
        float[] fa = new Rand.Pfloat().array(10);
        Arrays.stream(ConvertTo.boxed(fa));
        Arrays.stream(new Rand.Float().array(10));
    }
}
/* Output:
    eloztdv!
    ewcippc!
    ygpoalk!
    ljlbynx!
    taprwxz!
    47200
    61770
    84790
    66560
    37680
*/

 

数组排序

// arrays/Reverse.java
// The Collections.reverseOrder() Comparator

import onjava.*;

import java.util.Arrays;
import java.util.Collections;

import static onjava.ArrayShow.*;

public class Reverse {
    public static void main(String[] args) {
        CompType[] a = new CompType[12];
        Arrays.setAll(a, n -> CompType.get());
        show("Before sorting", a);
        Arrays.sort(a, Collections.reverseOrder());
        show("After sorting", a);
    }
}
/* Output:
Before sorting: [[i = 35, j = 37], [i = 41, j = 20],
                [i = 77, j = 79] , [i = 56, j = 68],
                [i = 48, j = 93], [i = 70, j = 7],
                [i = 0, j = 25], [i = 62, j = 34],
                [i = 50, j = 82] , [i = 31, j = 67],
                [i = 66, j = 54], [i = 21, j = 6] ]
After sorting: [[i = 77, j = 79], [i = 70, j = 7],
                [i = 66, j = 54] , [i = 62, j = 34],
                [i = 56, j = 68], [i = 50, j = 82] ,
                [i = 48, j = 93], [i = 41, j = 20],
                [i = 35, j = 37] , [i = 31, j = 67],
                [i = 21, j = 6], [i = 0, j = 25] ]
*/

自定义比较器

// arrays/ComparatorTest.java
// Implementing a Comparator for a class

import onjava.*;

import java.util.Arrays;
import java.util.Comparator;

import static onjava.ArrayShow.*;

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 = new CompType[12];
        Arrays.setAll(a, n -> CompType.get());
        show("Before sorting", a);
        Arrays.sort(a, new CompTypeComparator());
        show("After sorting", a);
    }
}
/* Output:
Before sorting:[[i = 35, j = 37], [i = 41, j = 20], [i = 77, j = 79] ,
                [i = 56, j = 68], [i = 48, j = 93], [i = 70, j = 7] ,
                [i = 0, j = 25], [i = 62, j = 34], [i = 50, j = 82],
                [i = 31, j = 67], [i = 66, j = 54], [i = 21, j = 6] ]
After sorting: [[i = 21, j = 6], [i = 70, j = 7], [i = 41, j = 20] ,
                [i = 0, j = 25], [i = 62, j = 34], [i = 35, j = 37] ,
                [i = 66, j = 54], [i = 31, j = 67], [i = 56, j = 68] ,
                [i = 77, j = 79], [i = 50, j = 82], [i = 48, j = 93] ]
*/

并行排序

// arrays/jmh/ParallelSort.java
package arrays.jmh;

import onjava.*;
import org.openjdk.jmh.annotations.*;

import java.util.Arrays;

@State(Scope.Thread)
public class ParallelSort {
    private long[] la;

    @Setup
    public void setup() {
        la = new Rand.Plong().array(100_000);
    }

    @Benchmark
    public void sort() {
        Arrays.sort(la);
    }

    @Benchmark
    public void parallelSort() {
        Arrays.parallelSort(la);
    }
}

parallelSort() 算法将大数组拆分成更小的数组,直到数组大小达到极限,然后使用普通的 Arrays .sort() 方法。然后合并结果。该算法需要不大于原始数组的额外工作空间。

 

parallelPrefix()并行前缀

// arrays/ParallelPrefix2.java

import onjava.*;

import java.util.Arrays;

import static onjava.ArrayShow.*;

public class ParallelPrefix2 {
    public static void main(String[] args) {
        String[] strings = new Rand.String(1).array(8);
        show(strings);
        Arrays.parallelPrefix(strings, (a, b) -> a + b);
        show(strings);
    }
}
/* Output:
[b, t, p, e, n, p, c, c]
[b, bt, btp, btpe, btpen, btpenp, btpenpc, btpenpcc]
*/

 

还是鼓励使用集合,因为低级数组实际上不太符合面向对象的思想。

posted @ 2019-12-08 19:56  zhangyu63  阅读(233)  评论(0编辑  收藏  举报