21-Arrays
第二十一章 数组
在 初始化和清理 一章的最后,你已经学过如何定义和初始化一个数组。
简单来看,数组需要你去创建和初始化,你可以通过下标对数组元素进行访问,数组的大小不会改变。大多数时候你只需要知道这些,但有时候你必须在数组上进行更复杂的操作,你也可能需要在数组和更加灵活的 集合
注意: 随着
数组特性
明明还有很多其他的办法来保存对象,那么是什么令数组如此特别?
将数组和其他类型的集合区分开来的原因有三:效率,类型,保存基本数据类型的能力。在
速度通常并不是问题,如果有问题,你保存和检索对象的方式也很少是罪魁祸首。你应该总是从
数组和集合
在泛型前,其他的集合类以一种宽泛的方式处理对象(就好像它们没有特定类型一样
当然,不管在编译时还是运行时,
一个数组可以保存基本数据类型,而一个预泛型的集合不可以。然而对于泛型而言,集合可以指定和检查他们保存对象的类型,而通过 自动装箱
下面给出一例用于比较数组和泛型集合:
// arrays/CollectionComparison.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
import java.util.*;
import onjava.*;
import static onjava.ArrayShow.*;
class BerylliumSphere {
private static long counter;
private final long id = counter++;
@Override
public String toString() {
return "Sphere " + id;
}
}
public class CollectionComparison {
public static void main(String[] args) {
BerylliumSphere[] spheres =
new BerylliumSphere[10];
for(int i = 0; i < 5; i++)
spheres[i] = new BerylliumSphere();
show(spheres);
System.out.println(spheres[4]);
List<BerylliumSphere> sphereList = Suppliers.create(
ArrayList::new, BerylliumSphere::new, 5);
System.out.println(sphereList);
System.out.println(sphereList.get(4));
int[] integers = { 0, 1, 2, 3, 4, 5 };
show(integers);
System.out.println(integers[4]);
List<Integer> intList = new ArrayList<>(
Arrays.asList(0, 1, 2, 3, 4, 5));
intList.add(97);
System.out.println(intList);
System.out.println(intList.get(4));
}
}
/* Output:
[Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4,
null, null, null, null, null]
Sphere 4
[Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9]
Sphere 9
[0, 1, 2, 3, 4, 5]
4
[0, 1, 2, 3, 4, 5, 97]
4
*/
add()
和 get()
等方法。数组和
用于显示数组的实用程序
在本章中,我们处处都要显示数组。
// onjava/ArrayShow.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package onjava;
import java.util.*;
public interface ArrayShow {
static void show(Object[] a) {
System.out.println(Arrays.toString(a));
}
static void show(boolean[] a) {
System.out.println(Arrays.toString(a));
}
static void show(byte[] a) {
System.out.println(Arrays.toString(a));
}
static void show(char[] a) {
System.out.println(Arrays.toString(a));
}
static void show(short[] a) {
System.out.println(Arrays.toString(a));
}
static void show(int[] a) {
System.out.println(Arrays.toString(a));
}
static void show(long[] a) {
System.out.println(Arrays.toString(a));
}
static void show(float[] a) {
System.out.println(Arrays.toString(a));
}
static void show(double[] a) {
System.out.println(Arrays.toString(a));
}
// Start with a description:
static void show(String info, Object[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, boolean[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, byte[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, char[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, short[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, int[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, long[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, float[] a) {
System.out.print(info + ": ");
show(a);
}
static void show(String info, double[] a) {
System.out.print(info + ": ");
show(a);
}
}
第一个方法适用于对象数组,包括那些包装基本数据类型的数组。所有的方法重载对于不同的数据类型是必要的。
第二组重载方法可以让你显示带有信息 字符串 前缀的数组。
为了简单起见,你通常可以静态地导入它们。
一等对象
不管你使用的什么类型的数组,数组中的数据集实际上都是对堆中真正对象的引用。数组是保存指向其他对象的引用的对象,数组可以隐式地创建,作为数组初始化语法的一部分,也可以显式地创建,比如使用一个
下面的例子总结了初始化数组的多种方式,并且展示了如何给不同的数组对象分配数组引用。同时也可以看出对象数组和基元数组在使用上是完全相同的。唯一的不同之处就是对象数组存储的是对象的引用,而基元数组则直接存储基本数据类型的值。
// 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
*/
数组
数组
下一个数组初始化可以被看做是一个“动态聚合初始化”。
hide(d);
你也可以动态地创建你用作参数传递的数组:
hide(new BerylliumSphere[]{
new BerlliumSphere(),
new BerlliumSphere()
});
很多情况下这种语法写代码更加方便。
表达式:
a = d;
显示了你如何获取指向一个数组对象的引用并将其分配给另一个数组对象。就像你可以处理其他类型的对象引用。现在
返回数组
假设你写了一个方法,这个方法不是返回一个元素,而是返回多个元素。对
而在
下面,我们返回一个 字符串 数组:
// arrays/IceCreamFlavors.java
// Returning arrays from methods
import java.util.*;
import static onjava.ArrayShow.*;
public class IceCreamFlavors {
private static SplittableRandom rand =
new SplittableRandom(47);
static final String[] FLAVORS = {
"Chocolate", "Strawberry", "Vanilla Fudge Swirl",
"Mint Chip", "Mocha Almond Fudge", "Rum Raisin",
"Praline Cream", "Mud Pie"
};
public static String[] flavorSet(int n) {
if(n > FLAVORS.length)
throw new IllegalArgumentException("Set too big");
String[] results = new String[n];
boolean[] picked = new boolean[FLAVORS.length];
for(int i = 0; i < n; i++) {
int t;
do
t = rand.nextInt(FLAVORS.length);
while(picked[t]);
results[i] = FLAVORS[t];
picked[t] = true;
}
return results;
}
public static void main(String[] args) {
for(int i = 0; i < 7; i++)
show(flavorSet(3));
}
}
/* Output:
[Praline Cream, Mint Chip, Vanilla Fudge Swirl]
[Strawberry, Vanilla Fudge Swirl, Mud Pie]
[Chocolate, Strawberry, Vanilla Fudge Swirl]
[Rum Raisin, Praline Cream, Chocolate]
[Mint Chip, Rum Raisin, Mocha Almond Fudge]
[Mocha Almond Fudge, Mud Pie, Vanilla Fudge Swirl]
[Mocha Almond Fudge, Mud Pie, Mint Chip]
*/
如果你必须要返回一系列不同类型的元素,你可以使用 泛型 中介绍的 元组 。
注意,当
比较将显示出随机选中的元素是不是已经存在于
一直到现在,随机数都是通过
多维数组
要创建多维的基元数组,你要用大括号来界定数组中的向量:
// arrays/MultidimensionalPrimitiveArray.java
import java.util.*;
public class MultidimensionalPrimitiveArray {
public static void main(String[] args) {
int[][] a = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
System.out.println(Arrays.deepToString(a));
}
}
/* Output:
[[1, 2, 3], [4, 5, 6]]
*/。
每个嵌套的大括号都代表了数组的一个维度。
这个例子使用
你也可以使用
// arrays/ThreeDWithNew.java
import java.util.*;
public class ThreeDWithNew {
public static void main(String[] args) {
// 3-D array with fixed length:
int[][][] a = new int[2][2][4];
System.out.println(Arrays.deepToString(a));
}
}
/* Output:
[[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0,
0]]]
*/
倘若你不对基元数组进行显式的初始化,它的值会自动初始化。而对象数组将被初始化为
组成矩阵的数组中每一个向量都可以是任意长度的(这叫做不规则数组
// 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], []]]
*/
第一个
[1] Java 8 增加了Arrays.setAll() 方法 , 其使用生成器来生成插入数组中的值。此生成器符合函数式接口IntUnaryOperator ,只使用一个非 默认 的方法ApplyAsint(int 操作数) 。Arrays.setAll() 传递当前数组索引作为操作数,因此一个选项是提供 n -> n 的 lambda 表达式来显示数组的索引(在上面的代码中很容易尝试) 。这里,我们忽略索引,只是插入递增计数器的值。
非基元的对象数组也可以定义为不规则数组。这里,我们收集了许多使用大括号的
// arrays/MultidimensionalObjectArrays.java
import java.util.*;
public class MultidimensionalObjectArrays {
public static void main(String[] args) {
BerylliumSphere[][] spheres = {
{ new BerylliumSphere(), new BerylliumSphere() },
{ new BerylliumSphere(), new BerylliumSphere(),
new BerylliumSphere(), new BerylliumSphere() },
{ new BerylliumSphere(), new BerylliumSphere(),
new BerylliumSphere(), new BerylliumSphere(),
new BerylliumSphere(), new BerylliumSphere(),
new BerylliumSphere(), new BerylliumSphere() },
};
System.out.println(Arrays.deepToString(spheres));
}
}
/* Output:
[[Sphere 0, Sphere 1], [Sphere 2, Sphere 3, Sphere 4,
Sphere 5], [Sphere 6, Sphere 7, Sphere 8, Sphere 9,
Sphere 10, Sphere 11, Sphere 12, Sphere 13]]
*/
数组初始化时使用自动装箱技术:
// arrays/AutoboxingArrays.java
import java.util.*;
public class AutoboxingArrays {
public static void main(String[] args) {
Integer[][] a = { // Autoboxing:
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
{ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
{ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 },
{ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 },
};
System.out.println(Arrays.deepToString(a));
}
}
/* Output:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [21, 22, 23, 24, 25,
26, 27, 28, 29, 30], [51, 52, 53, 54, 55, 56, 57, 58,
59, 60], [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]]
*/
以下是如何逐个构建非基元的对象数组:
// arrays/AssemblingMultidimensionalArrays.java
// Creating multidimensional arrays
import java.util.*;
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; // Autoboxing
}
System.out.println(Arrays.deepToString(a));
}
}
/* Output:
[[0, 0, 0], [0, 1, 2], [0, 2, 4]]
*/
// arrays/MultiDimWrapperArray.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Multidimensional arrays of "wrapper" objects
import java.util.*;
public class MultiDimWrapperArray {
public static void main(String[] args) {
Integer[][] a1 = { // Autoboxing
{ 1, 2, 3, },
{ 4, 5, 6, },
};
Double[][][] a2 = { // Autoboxing
{ { 1.1, 2.2 }, { 3.3, 4.4 } },
{ { 5.5, 6.6 }, { 7.7, 8.8 } },
{ { 9.9, 1.2 }, { 2.3, 3.4 } },
};
String[][] a3 = {
{ "The", "Quick", "Sly", "Fox" },
{ "Jumped", "Over" },
{ "The", "Lazy", "Brown", "Dog", "&", "friend" },
};
System.out.println(
"a1: " + Arrays.deepToString(a1));
System.out.println(
"a2: " + Arrays.deepToString(a2));
System.out.println(
"a3: " + Arrays.deepToString(a3));
}
}
/* Output:
a1: [[1, 2, 3], [4, 5, 6]]
a2: [[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7,
8.8]], [[9.9, 1.2], [2.3, 3.4]]]
a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The,
Lazy, Brown, Dog, &, friend]]
*/
同样的,在
泛型数组
一般来说,数组和泛型并不能很好的结合。你不能实例化参数化类型的数组:
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);
}
}
比起使用参数化类,使用参数化方法很方便。您不必为应用它的每个不同类型都实例化一个带有参数的类,但是可以使它成为 静态 的。你不能总是选择使用参数化方法而不用参数化的类,但通常参数化方法是更好的选择。
你不能创建泛型类型的数组,这种说法并不完全正确。是的,编译器不会让你 实例化 一个泛型的数组。但是,它将允许您创建对此类数组的引用。例如:
List<String>[] ls;
无可争议的,这可以通过编译。尽管不能创建包含泛型的实际数组对象,但是你可以创建一个非泛型的数组并对其进行强制类型转换:
// arrays/ArrayOfGenerics.java
import java.util.*;
public class ArrayOfGenerics {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<String>[] ls;
List[] la = new List[10];
ls = (List<String>[])la; // Unchecked cast
ls[0] = new ArrayList<>();
//- ls[1] = new ArrayList<Integer>();
// error: incompatible types: ArrayList<Integer>
// cannot be converted to List<String>
// ls[1] = new ArrayList<Integer>();
// ^
// The problem: List<String> is a subtype of Object
Object[] objects = ls; // So assignment is OK
// Compiles and runs without complaint:
objects[1] = new ArrayList<>();
// However, if your needs are straightforward it is
// possible to create an array of generics, albeit
// with an "unchecked cast" warning:
List<BerylliumSphere>[] spheres =
(List<BerylliumSphere>[])new List[10];
Arrays.setAll(spheres, n -> new ArrayList<>());
}
}
一旦你有了对
如果你知道你不会进行向上类型转换,你的需求相对简单,那么可以创建一个泛型数组,它将提供基本的编译时类型检查。然而,一个泛型
一般来说,您会发现泛型在类或方法的边界上是有效的。在内部,擦除常常会使泛型不可使用。所以,就像下面的例子,不能创建泛型类型的数组:
// arrays/ArrayOfGenericType.java
public class ArrayOfGenericType<T> {
T[] array; // OK
@SuppressWarnings("unchecked")
public ArrayOfGenericType(int size) {
// error: generic array creation:
//- array = new T[size];
array = (T[])new Object[size]; // unchecked cast
}
// error: generic array creation:
//- public <U> U[] makeArray() { return new U[10]; }
}
擦除再次从中作梗,这个例子试图创建已经擦除的类型数组,因此它们是未知的类型。你可以创建一个 对象 数组,然后对其进行强制类型转换,但如果没有
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]
*/
你既可以填充整个数组,也可以像最后两个语句所示,填充一系列的元素。但是由于你只能使用单个值调用
Arrays 的setAll 方法
在
- void setAll(int[] a, IntUnaryOperator gen)
- void setAll(long[] a, IntToLongFunction gen)
- void setAll(double[] a, IntToDoubleFunctiongen)
void setAll(T[] a, IntFunction<? extendsT> gen)
除了
// 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]
*/
[1] 这里,我们只是将数组索引作为值插入数组。这将自动转化为 long 和 double 版本。 [2] 这个函数只需要接受索引就能产生正确结果。这个,我们忽略索引值并且使用 val 生成结果。 [3] 方法引用有效,因为 Bob 的构造器接收一个 int 参数。只要我们传递的函数接收一个 int 参数且能产生正确的结果,就认为它完成了工作。 [4] 为了处理除了 int ,long ,double 之外的基元类型,请为基元创建包装类的数组。然后使用 setAll() 的泛型版本。请注意,getChar() 生成基元类型,因此这是自动装箱到 Character 。
增量生成
这是一个方法库,用于为不同类型生成增量值。
这些被作为内部类来生成容易记住的名字;比如,为了使用
注意,通过使用包装类的名称作为内部类名,我们必须调用
// onjava/Count.java
// Generate incremental values of different types
package onjava;
import java.util.*;
import java.util.function.*;
import static onjava.ConvertTo.*;
public interface Count {
class Boolean
implements Supplier<java.lang.Boolean> {
private boolean b = true;
@Override
public java.lang.Boolean get() {
b = !b;
return java.lang.Boolean.valueOf(b);
}
public java.lang.Boolean get(int n) {
return get();
}
public java.lang.Boolean[] array(int sz) {
java.lang.Boolean[] result =
new java.lang.Boolean[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pboolean {
private boolean b = true;
public boolean get() {
b = !b;
return b;
}
public boolean get(int n) { return get(); }
public boolean[] array(int sz) {
return primitive(new Boolean().array(sz));
}
}
class Byte
implements Supplier<java.lang.Byte> {
private byte b;
@Override
public java.lang.Byte get() { return b++; }
public java.lang.Byte get(int n) {
return get();
}
public java.lang.Byte[] array(int sz) {
java.lang.Byte[] result =
new java.lang.Byte[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pbyte {
private byte b;
public byte get() { return b++; }
public byte get(int n) { return get(); }
public byte[] array(int sz) {
return primitive(new Byte().array(sz));
}
}
char[] CHARS =
"abcdefghijklmnopqrstuvwxyz".toCharArray();
class Character
implements Supplier<java.lang.Character> {
private int i;
@Override
public java.lang.Character get() {
i = (i + 1) % CHARS.length;
return CHARS[i];
}
public java.lang.Character get(int n) {
return get();
}
public java.lang.Character[] array(int sz) {
java.lang.Character[] result =
new java.lang.Character[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pchar {
private int i;
public char get() {
i = (i + 1) % CHARS.length;
return CHARS[i];
}
public char get(int n) { return get(); }
public char[] array(int sz) {
return primitive(new Character().array(sz));
}
}
class Short
implements Supplier<java.lang.Short> {
short s;
@Override
public java.lang.Short get() { return s++; }
public java.lang.Short get(int n) {
return get();
}
public java.lang.Short[] array(int sz) {
java.lang.Short[] result =
new java.lang.Short[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pshort {
short s;
public short get() { return s++; }
public short get(int n) { return get(); }
public short[] array(int sz) {
return primitive(new Short().array(sz));
}
}
class Integer
implements Supplier<java.lang.Integer> {
int i;
@Override
public java.lang.Integer get() { return i++; }
public java.lang.Integer get(int n) {
return get();
}
public java.lang.Integer[] array(int sz) {
java.lang.Integer[] result =
new java.lang.Integer[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pint implements IntSupplier {
int i;
public int get() { return i++; }
public int get(int n) { return get(); }
@Override
public int getAsInt() { return get(); }
public int[] array(int sz) {
return primitive(new Integer().array(sz));
}
}
class Long
implements Supplier<java.lang.Long> {
private long l;
@Override
public java.lang.Long get() { return l++; }
public java.lang.Long get(int n) {
return get();
}
public java.lang.Long[] array(int sz) {
java.lang.Long[] result =
new java.lang.Long[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Plong implements LongSupplier {
private long l;
public long get() { return l++; }
public long get(int n) { return get(); }
@Override
public long getAsLong() { return get(); }
public long[] array(int sz) {
return primitive(new Long().array(sz));
}
}
class Float
implements Supplier<java.lang.Float> {
private int i;
@Override
public java.lang.Float get() {
return java.lang.Float.valueOf(i++);
}
public java.lang.Float get(int n) {
return get();
}
public java.lang.Float[] array(int sz) {
java.lang.Float[] result =
new java.lang.Float[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pfloat {
private int i;
public float get() { return i++; }
public float get(int n) { return get(); }
public float[] array(int sz) {
return primitive(new Float().array(sz));
}
}
class Double
implements Supplier<java.lang.Double> {
private int i;
@Override
public java.lang.Double get() {
return java.lang.Double.valueOf(i++);
}
public java.lang.Double get(int n) {
return get();
}
public java.lang.Double[] array(int sz) {
java.lang.Double[] result =
new java.lang.Double[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pdouble implements DoubleSupplier {
private int i;
public double get() { return i++; }
public double get(int n) { return get(); }
@Override
public double getAsDouble() { return get(0); }
public double[] array(int sz) {
return primitive(new Double().array(sz));
}
}
}
对于
这里是对
// arrays/TestCount.java
// Test counting generators
import java.util.*;
import java.util.stream.*;
import onjava.*;
import static onjava.ArrayShow.*;
public class TestCount {
static final int SZ = 5;
public static void main(String[] args) {
System.out.println("Boolean");
Boolean[] a1 = new Boolean[SZ];
Arrays.setAll(a1, new Count.Boolean()::get);
show(a1);
a1 = Stream.generate(new Count.Boolean())
.limit(SZ + 1).toArray(Boolean[]::new);
show(a1);
a1 = new Count.Boolean().array(SZ + 2);
show(a1);
boolean[] a1b =
new Count.Pboolean().array(SZ + 3);
show(a1b);
System.out.println("Byte");
Byte[] a2 = new Byte[SZ];
Arrays.setAll(a2, new Count.Byte()::get);
show(a2);
a2 = Stream.generate(new Count.Byte())
.limit(SZ + 1).toArray(Byte[]::new);
show(a2);
a2 = new Count.Byte().array(SZ + 2);
show(a2);
byte[] a2b = new Count.Pbyte().array(SZ + 3);
show(a2b);
System.out.println("Character");
Character[] a3 = new Character[SZ];
Arrays.setAll(a3, new Count.Character()::get);
show(a3);
a3 = Stream.generate(new Count.Character())
.limit(SZ + 1).toArray(Character[]::new);
show(a3);
a3 = new Count.Character().array(SZ + 2);
show(a3);
char[] a3b = new Count.Pchar().array(SZ + 3);
show(a3b);
System.out.println("Short");
Short[] a4 = new Short[SZ];
Arrays.setAll(a4, new Count.Short()::get);
show(a4);
a4 = Stream.generate(new Count.Short())
.limit(SZ + 1).toArray(Short[]::new);
show(a4);
a4 = new Count.Short().array(SZ + 2);
show(a4);
short[] a4b = new Count.Pshort().array(SZ + 3);
show(a4b);
System.out.println("Integer");
int[] a5 = new int[SZ];
Arrays.setAll(a5, new Count.Integer()::get);
show(a5);
Integer[] a5b =
Stream.generate(new Count.Integer())
.limit(SZ + 1).toArray(Integer[]::new);
show(a5b);
a5b = new Count.Integer().array(SZ + 2);
show(a5b);
a5 = IntStream.generate(new Count.Pint())
.limit(SZ + 1).toArray();
show(a5);
a5 = new Count.Pint().array(SZ + 3);
show(a5);
System.out.println("Long");
long[] a6 = new long[SZ];
Arrays.setAll(a6, new Count.Long()::get);
show(a6);
Long[] a6b = Stream.generate(new Count.Long())
.limit(SZ + 1).toArray(Long[]::new);
show(a6b);
a6b = new Count.Long().array(SZ + 2);
show(a6b);
a6 = LongStream.generate(new Count.Plong())
.limit(SZ + 1).toArray();
show(a6);
a6 = new Count.Plong().array(SZ + 3);
show(a6);
System.out.println("Float");
Float[] a7 = new Float[SZ];
Arrays.setAll(a7, new Count.Float()::get);
show(a7);
a7 = Stream.generate(new Count.Float())
.limit(SZ + 1).toArray(Float[]::new);
show(a7);
a7 = new Count.Float().array(SZ + 2);
show(a7);
float[] a7b = new Count.Pfloat().array(SZ + 3);
show(a7b);
System.out.println("Double");
double[] a8 = new double[SZ];
Arrays.setAll(a8, new Count.Double()::get);
show(a8);
Double[] a8b =
Stream.generate(new Count.Double())
.limit(SZ + 1).toArray(Double[]::new);
show(a8b);
a8b = new Count.Double().array(SZ + 2);
show(a8b);
a8 = DoubleStream.generate(new Count.Pdouble())
.limit(SZ + 1).toArray();
show(a8);
a8 = new Count.Pdouble().array(SZ + 3);
show(a8);
}
}
/* Output:
Boolean
[false, true, false, true, false]
[false, true, false, true, false, true]
[false, true, false, true, false, true, false]
[false, true, false, true, false, true, false, true]
Byte
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
Character
[b, c, d, e, f]
[b, c, d, e, f, g]
[b, c, d, e, f, g, h]
[b, c, d, e, f, g, h, i]
Short
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
Integer
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6, 7]
Long
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6, 7]
Float
[0.0, 1.0, 2.0, 3.0, 4.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
Double
[0.0, 1.0, 2.0, 3.0, 4.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
*/
注意到原始数组类型
通过
随机生成
我们可以按照
// onjava/Rand.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Generate random values of different types
package onjava;
import java.util.*;
import java.util.function.*;
import static onjava.ConvertTo.*;
public interface Rand {
int MOD = 10_000;
class Boolean
implements Supplier<java.lang.Boolean> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Boolean get() {
return r.nextBoolean();
}
public java.lang.Boolean get(int n) {
return get();
}
public java.lang.Boolean[] array(int sz) {
java.lang.Boolean[] result =
new java.lang.Boolean[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pboolean {
public boolean[] array(int sz) {
return primitive(new Boolean().array(sz));
}
}
class Byte
implements Supplier<java.lang.Byte> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Byte get() {
return (byte)r.nextInt(MOD);
}
public java.lang.Byte get(int n) {
return get();
}
public java.lang.Byte[] array(int sz) {
java.lang.Byte[] result =
new java.lang.Byte[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pbyte {
public byte[] array(int sz) {
return primitive(new Byte().array(sz));
}
}
class Character
implements Supplier<java.lang.Character> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Character get() {
return (char)r.nextInt('a', 'z' + 1);
}
public java.lang.Character get(int n) {
return get();
}
public java.lang.Character[] array(int sz) {
java.lang.Character[] result =
new java.lang.Character[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pchar {
public char[] array(int sz) {
return primitive(new Character().array(sz));
}
}
class Short
implements Supplier<java.lang.Short> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Short get() {
return (short)r.nextInt(MOD);
}
public java.lang.Short get(int n) {
return get();
}
public java.lang.Short[] array(int sz) {
java.lang.Short[] result =
new java.lang.Short[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pshort {
public short[] array(int sz) {
return primitive(new Short().array(sz));
}
}
class Integer
implements Supplier<java.lang.Integer> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Integer get() {
return r.nextInt(MOD);
}
public java.lang.Integer get(int n) {
return get();
}
public java.lang.Integer[] array(int sz) {
int[] primitive = new Pint().array(sz);
java.lang.Integer[] result =
new java.lang.Integer[sz];
for(int i = 0; i < sz; i++)
result[i] = primitive[i];
return result;
}
}
class Pint implements IntSupplier {
SplittableRandom r = new SplittableRandom(47);
@Override
public int getAsInt() {
return r.nextInt(MOD);
}
public int get(int n) { return getAsInt(); }
public int[] array(int sz) {
return r.ints(sz, 0, MOD).toArray();
}
}
class Long
implements Supplier<java.lang.Long> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Long get() {
return r.nextLong(MOD);
}
public java.lang.Long get(int n) {
return get();
}
public java.lang.Long[] array(int sz) {
long[] primitive = new Plong().array(sz);
java.lang.Long[] result =
new java.lang.Long[sz];
for(int i = 0; i < sz; i++)
result[i] = primitive[i];
return result;
}
}
class Plong implements LongSupplier {
SplittableRandom r = new SplittableRandom(47);
@Override
public long getAsLong() {
return r.nextLong(MOD);
}
public long get(int n) { return getAsLong(); }
public long[] array(int sz) {
return r.longs(sz, 0, MOD).toArray();
}
}
class Float
implements Supplier<java.lang.Float> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Float get() {
return (float)trim(r.nextDouble());
}
public java.lang.Float get(int n) {
return get();
}
public java.lang.Float[] array(int sz) {
java.lang.Float[] result =
new java.lang.Float[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
class Pfloat {
public float[] array(int sz) {
return primitive(new Float().array(sz));
}
}
static double trim(double d) {
return
((double)Math.round(d * 1000.0)) / 100.0;
}
class Double
implements Supplier<java.lang.Double> {
SplittableRandom r = new SplittableRandom(47);
@Override
public java.lang.Double get() {
return trim(r.nextDouble());
}
public java.lang.Double get(int n) {
return get();
}
public java.lang.Double[] array(int sz) {
double[] primitive =
new Rand.Pdouble().array(sz);
java.lang.Double[] result =
new java.lang.Double[sz];
for(int i = 0; i < sz; i++)
result[i] = primitive[i];
return result;
}
}
class Pdouble implements DoubleSupplier {
SplittableRandom r = new SplittableRandom(47);
@Override
public double getAsDouble() {
return trim(r.nextDouble());
}
public double get(int n) {
return getAsDouble();
}
public double[] array(int sz) {
double[] result = r.doubles(sz).toArray();
Arrays.setAll(result,
n -> result[n] = trim(result[n]));
return result;
}
}
class String
implements Supplier<java.lang.String> {
SplittableRandom r = new SplittableRandom(47);
private int strlen = 7; // Default length
public String() {}
public String(int strLength) {
strlen = strLength;
}
@Override
public java.lang.String get() {
return r.ints(strlen, 'a', 'z' + 1)
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append).toString();
}
public java.lang.String get(int n) {
return get();
}
public java.lang.String[] array(int sz) {
java.lang.String[] result =
new java.lang.String[sz];
Arrays.setAll(result, n -> get());
return result;
}
}
}
对于除了
下面是对所有
// arrays/TestRand.java
// Test random generators
import java.util.*;
import java.util.stream.*;
import onjava.*;
import static onjava.ArrayShow.*;
public class TestRand {
static final int SZ = 5;
public static void main(String[] args) {
System.out.println("Boolean");
Boolean[] a1 = new Boolean[SZ];
Arrays.setAll(a1, new Rand.Boolean()::get);
show(a1);
a1 = Stream.generate(new Rand.Boolean())
.limit(SZ + 1).toArray(Boolean[]::new);
show(a1);
a1 = new Rand.Boolean().array(SZ + 2);
show(a1);
boolean[] a1b =
new Rand.Pboolean().array(SZ + 3);
show(a1b);
System.out.println("Byte");
Byte[] a2 = new Byte[SZ];
Arrays.setAll(a2, new Rand.Byte()::get);
show(a2);
a2 = Stream.generate(new Rand.Byte())
.limit(SZ + 1).toArray(Byte[]::new);
show(a2);
a2 = new Rand.Byte().array(SZ + 2);
show(a2);
byte[] a2b = new Rand.Pbyte().array(SZ + 3);
show(a2b);
System.out.println("Character");
Character[] a3 = new Character[SZ];
Arrays.setAll(a3, new Rand.Character()::get);
show(a3);
a3 = Stream.generate(new Rand.Character())
.limit(SZ + 1).toArray(Character[]::new);
show(a3);
a3 = new Rand.Character().array(SZ + 2);
show(a3);
char[] a3b = new Rand.Pchar().array(SZ + 3);
show(a3b);
System.out.println("Short");
Short[] a4 = new Short[SZ];
Arrays.setAll(a4, new Rand.Short()::get);
show(a4);
a4 = Stream.generate(new Rand.Short())
.limit(SZ + 1).toArray(Short[]::new);
show(a4);
a4 = new Rand.Short().array(SZ + 2);
show(a4);
short[] a4b = new Rand.Pshort().array(SZ + 3);
show(a4b);
System.out.println("Integer");
int[] a5 = new int[SZ];
Arrays.setAll(a5, new Rand.Integer()::get);
show(a5);
Integer[] a5b =
Stream.generate(new Rand.Integer())
.limit(SZ + 1).toArray(Integer[]::new);
show(a5b);
a5b = new Rand.Integer().array(SZ + 2);
show(a5b);
a5 = IntStream.generate(new Rand.Pint())
.limit(SZ + 1).toArray();
show(a5);
a5 = new Rand.Pint().array(SZ + 3);
show(a5);
System.out.println("Long");
long[] a6 = new long[SZ];
Arrays.setAll(a6, new Rand.Long()::get);
show(a6);
Long[] a6b = Stream.generate(new Rand.Long())
.limit(SZ + 1).toArray(Long[]::new);
show(a6b);
a6b = new Rand.Long().array(SZ + 2);
show(a6b);
a6 = LongStream.generate(new Rand.Plong())
.limit(SZ + 1).toArray();
show(a6);
a6 = new Rand.Plong().array(SZ + 3);
show(a6);
System.out.println("Float");
Float[] a7 = new Float[SZ];
Arrays.setAll(a7, new Rand.Float()::get);
show(a7);
a7 = Stream.generate(new Rand.Float())
.limit(SZ + 1).toArray(Float[]::new);
show(a7);
a7 = new Rand.Float().array(SZ + 2);
show(a7);
float[] a7b = new Rand.Pfloat().array(SZ + 3);
show(a7b);
System.out.println("Double");
double[] a8 = new double[SZ];
Arrays.setAll(a8, new Rand.Double()::get);
show(a8);
Double[] a8b =
Stream.generate(new Rand.Double())
.limit(SZ + 1).toArray(Double[]::new);
show(a8b);
a8b = new Rand.Double().array(SZ + 2);
show(a8b);
a8 = DoubleStream.generate(new Rand.Pdouble())
.limit(SZ + 1).toArray();
show(a8);
a8 = new Rand.Pdouble().array(SZ + 3);
show(a8);
System.out.println("String");
String[] s = new String[SZ - 1];
Arrays.setAll(s, new Rand.String()::get);
show(s);
s = Stream.generate(new Rand.String())
.limit(SZ).toArray(String[]::new);
show(s);
s = new Rand.String().array(SZ + 1);
show(s);
Arrays.setAll(s, new Rand.String(4)::get);
show(s);
s = Stream.generate(new Rand.String(4))
.limit(SZ).toArray(String[]::new);
show(s);
s = new Rand.String(4).array(SZ + 1);
show(s);
}
}
/* Output:
Boolean
[true, false, true, true, true]
[true, false, true, true, true, false]
[true, false, true, true, true, false, false]
[true, false, true, true, true, false, false, true]
Byte
[123, 33, 101, 112, 33]
[123, 33, 101, 112, 33, 31]
[123, 33, 101, 112, 33, 31, 0]
[123, 33, 101, 112, 33, 31, 0, -72]
Character
[b, t, p, e, n]
[b, t, p, e, n, p]
[b, t, p, e, n, p, c]
[b, t, p, e, n, p, c, c]
Short
[635, 8737, 3941, 4720, 6177]
[635, 8737, 3941, 4720, 6177, 8479]
[635, 8737, 3941, 4720, 6177, 8479, 6656]
[635, 8737, 3941, 4720, 6177, 8479, 6656, 3768]
Integer
[635, 8737, 3941, 4720, 6177]
[635, 8737, 3941, 4720, 6177, 8479]
[635, 8737, 3941, 4720, 6177, 8479, 6656]
[635, 8737, 3941, 4720, 6177, 8479]
[635, 8737, 3941, 4720, 6177, 8479, 6656, 3768]
Long
[6882, 3765, 692, 9575, 4439]
[6882, 3765, 692, 9575, 4439, 2638]
[6882, 3765, 692, 9575, 4439, 2638, 4011]
[6882, 3765, 692, 9575, 4439, 2638]
[6882, 3765, 692, 9575, 4439, 2638, 4011, 9610]
Float
[4.83, 2.89, 2.9, 1.97, 3.01]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18, 0.99]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18, 0.99, 8.28]
Double
[4.83, 2.89, 2.9, 1.97, 3.01]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18, 0.99]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18, 0.99, 8.28]
String
[btpenpc, cuxszgv, gmeinne, eloztdv]
[btpenpc, cuxszgv, gmeinne, eloztdv, ewcippc]
[btpenpc, cuxszgv, gmeinne, eloztdv, ewcippc, ygpoalk]
[btpe, npcc, uxsz, gvgm, einn, eelo]
[btpe, npcc, uxsz, gvgm, einn]
[btpe, npcc, uxsz, gvgm, einn, eelo]
*/
注意(除了
泛型和基本数组
在本章的前面,我们被提醒,泛型不能和基元一起工作。在这种情况下,我们必须从基元数组转换为包装类型的数组,并且还必须从另一个方向转换。下面是一个转换器可以同时对所有类型的数据执行操作:
// onjava/ConvertTo.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
package onjava;
public interface ConvertTo {
static boolean[] primitive(Boolean[] in) {
boolean[] result = new boolean[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i]; // Autounboxing
return result;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
// Convert from primitive array to wrapped array:
static Boolean[] boxed(boolean[] in) {
Boolean[] result = new Boolean[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i]; // Autoboxing
return result;
}
static Character[] boxed(char[] in) {
Character[] result = new Character[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Byte[] boxed(byte[] in) {
Byte[] result = new Byte[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Short[] boxed(short[] in) {
Short[] result = new Short[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Integer[] boxed(int[] in) {
Integer[] result = new Integer[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Long[] boxed(long[] in) {
Long[] result = new Long[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Float[] boxed(float[] in) {
Float[] result = new Float[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
static Double[] boxed(double[] in) {
Double[] result = new Double[in.length];
for(int i = 0; i < in.length; i++)
result[i] = in[i];
return result;
}
}
下面是对
// arrays/TestConvertTo.java
import java.util.*;
import onjava.*;
import static onjava.ArrayShow.*;
import static onjava.ConvertTo.*;
public class TestConvertTo {
static final int SIZE = 6;
public static void main(String[] args) {
Boolean[] a1 = new Boolean[SIZE];
Arrays.setAll(a1, new Rand.Boolean()::get);
boolean[] a1p = primitive(a1);
show("a1p", a1p);
Boolean[] a1b = boxed(a1p);
show("a1b", a1b);
Byte[] a2 = new Byte[SIZE];
Arrays.setAll(a2, new Rand.Byte()::get);
byte[] a2p = primitive(a2);
show("a2p", a2p);
Byte[] a2b = boxed(a2p);
show("a2b", a2b);
Character[] a3 = new Character[SIZE];
Arrays.setAll(a3, new Rand.Character()::get);
char[] a3p = primitive(a3);
show("a3p", a3p);
Character[] a3b = boxed(a3p);
show("a3b", a3b);
Short[] a4 = new Short[SIZE];
Arrays.setAll(a4, new Rand.Short()::get);
short[] a4p = primitive(a4);
show("a4p", a4p);
Short[] a4b = boxed(a4p);
show("a4b", a4b);
Integer[] a5 = new Integer[SIZE];
Arrays.setAll(a5, new Rand.Integer()::get);
int[] a5p = primitive(a5);
show("a5p", a5p);
Integer[] a5b = boxed(a5p);
show("a5b", a5b);
Long[] a6 = new Long[SIZE];
Arrays.setAll(a6, new Rand.Long()::get);
long[] a6p = primitive(a6);
show("a6p", a6p);
Long[] a6b = boxed(a6p);
show("a6b", a6b);
Float[] a7 = new Float[SIZE];
Arrays.setAll(a7, new Rand.Float()::get);
float[] a7p = primitive(a7);
show("a7p", a7p);
Float[] a7b = boxed(a7p);
show("a7b", a7b);
Double[] a8 = new Double[SIZE];
Arrays.setAll(a8, new Rand.Double()::get);
double[] a8p = primitive(a8);
show("a8p", a8p);
Double[] a8b = boxed(a8p);
show("a8b", a8b);
}
}
/* Output:
a1p: [true, false, true, true, true, false]
a1b: [true, false, true, true, true, false]
a2p: [123, 33, 101, 112, 33, 31]
a2b: [123, 33, 101, 112, 33, 31]
a3p: [b, t, p, e, n, p]
a3b: [b, t, p, e, n, p]
a4p: [635, 8737, 3941, 4720, 6177, 8479]
a4b: [635, 8737, 3941, 4720, 6177, 8479]
a5p: [635, 8737, 3941, 4720, 6177, 8479]
a5b: [635, 8737, 3941, 4720, 6177, 8479]
a6p: [6882, 3765, 692, 9575, 4439, 2638]
a6b: [6882, 3765, 692, 9575, 4439, 2638]
a7p: [4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
a7b: [4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
a8p: [4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
a8b: [4.83, 2.89, 2.9, 1.97, 3.01, 0.18]
*/
在每种情况下,原始数组都是为包装类型创建的,并使用
数组元素修改
传递给
// arrays/ModifyExisting.java
import java.util.*;
import onjava.*;
import static onjava.ArrayShow.*;
public class ModifyExisting {
public static void main(String[] args) {
double[] da = new double[7];
Arrays.setAll(da, new Rand.Double()::get);
show(da);
Arrays.setAll(da, n -> da[n] / 100); // [1]
show(da);
}
}
/* Output:
[4.83, 2.89, 2.9, 1.97, 3.01, 0.18, 0.99]
[0.0483, 0.028900000000000002, 0.028999999999999998,
0.0197, 0.0301, 0.0018, 0.009899999999999999]
*/
数组并行
我们很快就不得不面对并行的主题。例如
在阅读并发编程这一章之后,您将更深入地理解它
最好从数据的角度来考虑并行性。对于大量数据
在本书的其余部分,我们将遇到不同的情况
-
1、所提供的惟一选项是并行的。这很简单,因为我们别无选择,只能使用它。这种情况是比较罕见的。
-
2、有多个选项,但是并行版本
( 通常是最新的版本) 被设计成在任何地方都可以使用( 甚至在那些不关心并行性的代码中) ,如案例#1 。我们将按预期使用并行版本。 -
3、案例
1 和案例2 并不经常发生。相反,您将遇到某些算法的两个版本,一个用于并行使用,另一个用于正常使用。我将描述并行的一个,但不会在普通代码中使用它,因为它也许会产生所有可能的问题。
我建议您在自己的代码中采用这种方法。
parallelSetAll()
流式编程产生优雅的代码。例如,假设我们想要创建一个数值由从零开始填充的长数组:
// arrays/CountUpward.java
import java.util.stream.LongStream;
public class CountUpward {
static long[] fillCounted(int size) {
return LongStream.iterate(0, i -> i + 1).limit(size).toArray();
}
public static void main(String[] args) {
long[] l1 = fillCounted(20); // No problem
show(l1);
// On my machine, this runs out of heap space:
// - long[] l2 = fillCounted(10_000_000);
}
}
/* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19]
*/
流 实际上可以存储到将近
// 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/ArrayCopying.java
// Demonstrate Arrays.copy() and Arrays.copyOf()
import onjava.*;
import java.util.Arrays;
import static onjava.ArrayShow.*;
class Sup {
// Superclass
private int id;
Sup(int n) {
id = n;
}
@Override
public String toString() {
return getClass().getSimpleName() + id;
}
}
class Sub extends Sup { // Subclass
Sub(int n) {
super(n);
}
}
public class ArrayCopying {
public static final int SZ = 15;
public static void main(String[] args) {
int[] a1 = new int[SZ];
Arrays.setAll(a1, new Count.Integer()::get);
show("a1", a1);
int[] a2 = Arrays.copyOf(a1, a1.length); // [1]
// Prove they are distinct arrays:
Arrays.fill(a1, 1);
show("a1", a1);
show("a2", a2);
// Create a shorter result:
a2 = Arrays.copyOf(a2, a2.length / 2); // [2]
show("a2", a2);
// Allocate more space:
a2 = Arrays.copyOf(a2, a2.length + 5);
show("a2", a2);
// Also copies wrapped arrays:
Integer[] a3 = new Integer[SZ]; // [3]
Arrays.setAll(a3, new Count.Integer()::get);
Integer[] a4 = Arrays.copyOfRange(a3, 4, 12);
show("a4", a4);
Sub[] d = new Sub[SZ / 2];
Arrays.setAll(d, Sub::new); // Produce Sup[] from Sub[]:
Sup[] b = Arrays.copyOf(d, d.length, Sup[].class); // [4]
show(b); // This "downcast" works fine:
Sub[] d2 = Arrays.copyOf(b, b.length, Sub[].class); // [5]
show(d2); // Bad "downcast" compiles but throws exception:
Sup[] b2 = new Sup[SZ / 2];
Arrays.setAll(b2, Sup::new);
try {
Sub[] d3 = Arrays.copyOf(b2, b2.length, Sub[].class); // [6]
} catch (Exception e) {
System.out.println(e);
}
}
}
/* Output: a1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] a1: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
a2:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]a2:[0, 1, 2, 3, 4, 5, 6]a2:[
0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0]a4:[4, 5, 6, 7, 8, 9, 10, 11][Sub0, Sub1, Sub2, Sub3, Sub4, Sub5, Sub6][
Sub0, Sub1, Sub2, Sub3, Sub4, Sub5, Sub6]java.lang.ArrayStoreException */
实例表明,原生数组和对象数组都可以被复制。但是,如果复制对象的数组,那么只复制引用—不复制对象本身。这称为浅拷贝
还有一个方法
数组比较
数组 提供了
数组相等的含义:数组必须有相同数量的元素,并且每个元素必须与另一个数组中的对应元素相等,对每个元素使用
// arrays/ComparingArrays.java
// Using Arrays.equals()
import java.util.*;
import onjava.*;
public class ComparingArrays {
public static final int SZ = 15;
static String[][] twoDArray() {
String[][] md = new String[5][];
Arrays.setAll(md, n -> new String[n]);
for (int i = 0; i < md.length; i++) Arrays.setAll(md[i], new Rand.String()::get);
return md;
}
public static void main(String[] args) {
int[] a1 = new int[SZ], a2 = new int[SZ];
Arrays.setAll(a1, new Count.Integer()::get);
Arrays.setAll(a2, new Count.Integer()::get);
System.out.println("a1 == a2: " + Arrays.equals(a1, a2));
a2[3] = 11;
System.out.println("a1 == a2: " + Arrays.equals(a1, a2));
Integer[] a1w = new Integer[SZ], a2w = new Integer[SZ];
Arrays.setAll(a1w, new Count.Integer()::get);
Arrays.setAll(a2w, new Count.Integer()::get);
System.out.println("a1w == a2w: " + Arrays.equals(a1w, a2w));
a2w[3] = 11;
System.out.println("a1w == a2w: " + Arrays.equals(a1w, a2w));
String[][] md1 = twoDArray(), md2 = twoDArray();
System.out.println(Arrays.deepToString(md1));
System.out.println("deepEquals(md1, md2): " + Arrays.deepEquals(md1, md2));
System.out.println("md1 == md2: " + Arrays.equals(md1, md2));
md1[4][1] = "#$#$#$#";
System.out.println(Arrays.deepToString(md1));
System.out.println("deepEquals(md1, md2): " + Arrays.deepEquals(md1, md2));
}
}
/* Output:
a1 == a2: true
a1 == a2: false
a1w == a2w: true
a1w == a2w: false
[[], [btpenpc], [btpenpc, cuxszgv], [btpenpc, cuxszgv,
gmeinne], [btpenpc, cuxszgv, gmeinne, eloztdv]]
deepEquals(md1, md2): true
md1 == md2: false
[[], [btpenpc], [btpenpc, cuxszgv], [btpenpc, cuxszgv,
gmeinne], [btpenpc, #$#$#$#, gmeinne, eloztdv]]
deepEquals(md1, md2): false
*/
最初,
流和数组
// 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
*/
只有“原生类型” int、
通常,将数组转换为流来生成所需的结果要比直接操作数组容易得多。请注意,即使流已经“用完”
数组排序
根据对象的实际类型执行比较排序。一种方法是为不同的类型编写对应的排序方法,但是这样的代码不能复用。
编程设计的一个主要目标是“将易变的元素与稳定的元素分开”,在这里,保持不变的代码是一般的排序算法,但是变化的是对象的比较方式。因此,使用策略设计模式而不是将比较代码放入许多不同的排序源码中。使用策略模式时,变化的代码部分被封装在一个单独的类
您将一个策略对象交给相同的代码,该代码使用策略模式来实现其算法。通过这种方式,您将使用相同的排序代码,使不同的对象表达不同的比较方式。
这里有一个类,它实现了
// arrays/CompType.java
// Implementing Comparable in a class
import onjava.*;
import java.util.Arrays;
import java.util.SplittableRandom;
import static onjava.ArrayShow.*;
public class CompType implements Comparable<CompType> {
private static int count = 1;
private static SplittableRandom r = new SplittableRandom(47);
int i;
int j;
public CompType(int n1, int n2) {
i = n1;
j = n2;
}
public static CompType get() {
return new CompType(r.nextInt(100), r.nextInt(100));
}
public static void main(String[] args) {
CompType[] a = new CompType[12];
Arrays.setAll(a, n -> get());
show("Before sorting", a);
Arrays.sort(a);
show("After sorting", a);
}
@Override
public String toString() {
String result = "[i = " + i + ", j = " + j + "]";
if (count++ % 3 == 0) result += "\n";
return result;
}
@Override
public int compareTo(CompType rv) {
return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
}
}
/* 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 = 0, j = 25], [i = 21, j = 6], [i = 31, j = 67] ,
[i = 35, j = 37], [i = 41, j = 20], [i = 48, j = 93] ,
[i = 50, j = 82], [i = 56, j = 68], [i = 62, j = 34] ,
[i = 66, j = 54], [i = 70, j = 7], [i = 77, j = 79] ]
*/
当您定义比较方法时,您有责任决定将一个对象与另一个对象进行比较意味着什么。这里,在比较中只使用
现在假设有人给了你一个没有实现
集合类
// 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.sort 的使用
使用内置的排序方法,您可以对实现了
// arrays/StringSorting.java
// Sorting an array of Strings
import onjava.*;
import java.util.Arrays;
import java.util.Collections;
import static onjava.ArrayShow.*;
public class StringSorting {
public static void main(String[] args) {
String[] sa = new Rand.String().array(20);
show("Before sort", sa);
Arrays.sort(sa);
show("After sort", sa);
Arrays.sort(sa, Collections.reverseOrder());
show("Reverse sort", sa);
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
show("Case-insensitive sort", sa);
}
}
/* Output:
Before sort: [btpenpc, cuxszgv, gmeinne, eloztdv, ewcippc,
ygpoalk, ljlbynx, taprwxz, bhmupju, cjwzmmr,
anmkkyh, fcjpthl, skddcat, jbvlgwc, mvducuj,
ydpulcq, zehpfmm, zrxmclh, qgekgly, hyoubzl]
After sort: [anmkkyh, bhmupju, btpenpc, cjwzmmr, cuxszgv,
eloztdv, ewcippc, fcjpthl, gmeinne, hyoubzl,
jbvlgwc, ljlbynx, mvducuj, qgekgly, skddcat,
taprwxz, ydpulcq, ygpoalk, zehpfmm, zrxmclh]
Reverse sort: [zrxmclh, zehpfmm, ygpoalk, ydpulcq,taprwxz,
skddcat, qgekgly, mvducuj, ljlbynx, jbvlgwc,
hyoubzl, gmeinne, fcjpthl, ewcippc, eloztdv,
cuxszgv, cjwzmmr, btpenpc, bhmupju, anmkkyh]
Case-insensitive sort: [anmkkyh, bhmupju, btpenpc, cjwzmmr,
cuxszgv, eloztdv, ewcippc, fcjpthl, gmeinne,
hyoubzl, jbvlgwc, ljlbynx, mvducuj, qgekgly,
skddcat, taprwxz, ydpulcq, ygpoalk, zehpfmm, zrxmclh]
*/
注意字符串排序算法中的输出。它是字典式的,所以它把所有以大写字母开头的单词放在前面,然后是所有以小写字母开头的单词。
并行排序
如果排序性能是一个问题,那么可以使用
// 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);
}
}
您可能会看到不同的结果,但是在我的机器上,并行排序将速度提高了大约
binarySearch 二分查找
一旦数组被排序,您就可以通过使用
// arrays/ArraySearching.java
// Using Arrays.binarySearch()
import onjava.*;
import java.util.Arrays;
import static onjava.ArrayShow.*;
public class ArraySearching {
public static void main(String[] args) {
Rand.Pint rand = new Rand.Pint();
int[] a = new Rand.Pint().array(25);
Arrays.sort(a);
show("Sorted array", a);
while (true) {
int r = rand.getAsInt();
int location = Arrays.binarySearch(a, r);
if (location >= 0) {
System.out.println("Location of " + r + " is " + location + ", a[" + location + "] is " + a[location]);
break; // Out of while loop
}
}
}
}
/* Output:
Sorted array: [125, 267, 635, 650, 1131, 1506, 1634, 2400, 2766,
3063, 3768, 3941, 4720, 4762, 4948, 5070, 5682,
5807, 6177, 6193, 6656, 7021, 8479, 8737, 9954]
Location of 635 is 2, a[2] is 635
*/
在
如果找到了搜索项,
如果数组包含重复的元素,则无法保证找到其中的那些重复项。搜索算法不是为了支持重复的元素,而是为了容忍它们。如果需要没有重复元素的排序列表,可以使用
如果使用比较器
// arrays/AlphabeticSearch.java
// Searching with a Comparator
import onjava.*;
import java.util.Arrays;
import static onjava.ArrayShow.*;
public class AlphabeticSearch {
public static void main(String[] args) {
String[] sa = new Rand.String().array(30);
Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
show(sa);
int index = Arrays.binarySearch(sa, sa[10], String.CASE_INSENSITIVE_ORDER);
System.out.println("Index: " + index + "\n" + sa[index]);
}
}
/* Output:
[anmkkyh, bhmupju, btpenpc, cjwzmmr, cuxszgv, eloztdv, ewcippc,
ezdeklu, fcjpthl, fqmlgsh, gmeinne, hyoubzl, jbvlgwc, jlxpqds,
ljlbynx, mvducuj, qgekgly, skddcat, taprwxz, uybypgp, vjsszkn,
vniyapk, vqqakbm, vwodhcf, ydpulcq, ygpoalk, yskvett, zehpfmm,
zofmmvm, zrxmclh]
Index: 10 gmeinne
*/
比较器必须作为第三个参数传递给重载的
parallelPrefix 并行前缀
没有“prefix()”方法,只有
// arrays/ParallelPrefix1.java
import onjava.*;
import java.util.Arrays;
import static onjava.ArrayShow.*;
public class ParallelPrefix1 {
public static void main(String[] args) {
int[] nums = new Count.Pint().array(10);
show(nums);
System.out.println(Arrays.stream(nums).reduce(Integer::sum).getAsInt());
Arrays.parallelPrefix(nums, Integer::sum);
show(nums);
System.out.println(Arrays.stream(new Count.Pint().array(6)).reduce(Integer::sum).getAsInt());
}
}
/* Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
45
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
15
*/
这里我们对数组应用
使用
使用字符串可能更清楚
// 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]
*/
如前所述,使用流进行初始化非常优雅,但是对于大型数组,这种方法可能会耗尽堆空间。使用
// arrays/ParallelPrefix3.java
// {ExcludeFromTravisCI}
import java.util.Arrays;
public class ParallelPrefix3 {
static final int SIZE = 10_000_000;
public static void main(String[] args) {
long[] nums = new long[SIZE];
Arrays.setAll(nums, n -> n);
Arrays.parallelPrefix(nums, Long::sum);
System.out.println("First 20: " + nums[19]);
System.out.println("First 200: " + nums[199]);
System.out.println("All: " + nums[nums.length - 1]);
}
}
/* Output:
First 20: 190
First 200: 19900
All: 49999995000000
*/
因为正确使用
本章小结
在
使用自动装箱和泛型,在集合中保存原生类型是毫不费力的,这进一步鼓励您用集合替换低级数组。由于泛型产生类型安全的集合,数组在这方面也不再有优势。
如本章所述,当您尝试使用泛型时,您将看到泛型对数组是相当不友好的。通常,即使可以让泛型和数组以某种形式一起工作
有几次,当我们讨论特定的例子时,我直接从
所有这些问题都表明,在使用
# arrays/PythonLists.py
aList=[1,2,3,4,5]print(type(aList)) #<type 'list'>
print(aList) # [1,2,3,4,5]
print(aList[4]) # 5Basic list indexing
aList.append(6) # lists can be resized
aList+=[7,8] # Add a list to a list
print(aList) # [1,2,3,4,5,6,7,8]
aSlice=aList[2:4]
print(aSlice) # [3,4]
class MyList(list): # Inherit from list
# Define a method;'this'pointer is explicit:
def getReversed(self):
reversed=self[:] # Copy list using slices
reversed.reverse() # Built-in list method
return reversed
# No'new'necessary for object creation:
list2=MyList(aList)
print(type(list2)) #<class '__main__.MyList'>
print(list2.getReversed()) # [8,7,6,5,4,3,2,1]
output="""
<class 'list'>
[1, 2, 3, 4, 5]
5
[1, 2, 3, 4, 5, 6, 7, 8]
[3, 4]
<class '__main__.MyList'>
[8, 7, 6, 5, 4, 3, 2, 1]
"""
前一章介绍了基本的
虽然一切在
在经历了这么多年的
撇开““Green-fields”的推测不谈,我们肯定会被数组所困扰,当你阅读代码时就会看到它们。然而,集合几乎总是更好的选择。