到目前为止,我们已经将 Integer、Long 收集到了一个 Stream<Integer>、Stream<Long> 的流中,不过将每个整数包装成相应对象显然是一个低效的做法,对于其他原始类型 double、float、long、short、char、byte 及 boolean 也是一样。
为此,Java8 Stream API 提供了 IntStream、LongStream 和 DoubleStream 等类型,专门用来直接存储原始类型值,不必使用包装。
如果你想要存储 short、char、byte 和 boolean 类型的值,请使用 IntStream;而如果要存储 float 类型的值,请使用 DoubleStream。这是因为 Stream API 的设计者们认为,不需要为其他 5 种原始类型都添加对应的专门类型。
要创建一个 IntStream,你可以调用 Intstream.of 和 Arrays.stream 方法:
IntStream intStream = IntStream.of(1, 2, 3); intStream.max().ifPresent(System.out::println); //输出:3 Arrays.stream(new int[]{1, 2, 3}).max().ifPresent(System.out::println); //输出:3
对于对象流,你还可以使用静态的生成和迭代方法。除此之外,IntStream 和 LongStream 还拥有静态方法 range 和 rangeclosed,用来产生步长为1的一个整数范围。例如:
IntStream zeroToNinetyNine = IntStream.range(0,100); // 不包括上限 IntStream System.out.println("sum=" + zeroToNinetyNine.sum());//sum=4950 IntStream zeroToHundred = IntStream.rangeClosed(0,100); // 包括上限 System.out.println("sum=" + zeroToHundred.sum()); //sum=5050
CharSequence 接口有两个方法 codePoints 和 chars,可以生成包含字符 Unicode 代码的流,或者是包含 UTF-16 编码的代码单元的 IntStream。例如:
// \uD835\uDD46 是字母 ① 的 UTF-16 编码,unicode 是 U+1D546 String sentence = "\uD835\uDD46 is the set of octonions."; IntStream codes = sentence.codePoints(); codes.forEach(e -> { System.out.println(e + " = " + Integer.toHexString(e) + " = " + (char) e); }); //输出: //120134 = 1d546 = 핆 //32 = 20 = //105 = 69 = i //115 = 73 = s //32 = 20 = //116 = 74 = t //104 = 68 = h //101 = 65 = e //32 = 20 = //115 = 73 = s //101 = 65 = e //116 = 74 = t //32 = 20 = //111 = 6f = o //102 = 66 = f //32 = 20 = //111 = 6f = o //99 = 63 = c //116 = 74 = t //111 = 6f = o //110 = 6e = n //105 = 69 = i //111 = 6f = o //110 = 6e = n //115 = 73 = s //46 = 2e = .
当你拥有一个对象流的时候,你可以通过 mapToInt、mapToLong 或者 mapToDouble 方法将它转换为一个原始类型流。例如,如果你有一个字符串流,并且希望按照它们的长度 (整型) 进行处理时,你可能会使用一个 IntStream:
List<String> list = Arrays.asList("one", "two", "three"); Stream<String> stream = list.stream(); int[] arrays = stream.mapToInt(String::length).toArray(); System.out.println(Arrays.toString(arrays)); //输出:[3, 3, 5]
要将一个原始类型流转换为一个对象流,可以使用 boxed 方法:
List<String> list = Arrays.asList("one", "two", "three"); Stream<String> stream = list.stream(); // 得到原始类型流 IntStream intStream = stream.mapToInt(String::length); // 将原始类型流转换为对象流 Stream<Integer> integerStream = intStream.boxed(); System.out.println(integerStream.collect(Collectors.toList())); //输出:[3, 3, 5]
一般来说,原始类型流上的方法与在对象流上调用的方法类似,但是有以下几点显著的区别:
toArray 方法会返回一个原始类型的数组,如:int[]。
产生 Optional 结果的方法会返回一个 OptionalInt、OptionalLong 或者 OptionalDouble 类型。这些类与 Optional 类类似,但是它们没有 get 方法,而是用对应的 getAsInt、getAsLong 和 getAsDouble 来代替。例如:
List<String> list = Arrays.asList("one", "two", "three"); OptionalInt opt = list.stream().mapToInt(String::length).max(); System.out.println(opt.getAsInt()); // 5
方法 sum、average、max 和 min 会返回总和、平均值、最大值和最小值。在对象流中没有定义这些方法。
summaryStatistics 方法会产生一个 IntSummaryStatistics、LongSummaryStatistics 或者DoubleStummaryStatistics 对象,可以同时获得原始类型流的总和、平均值、最大值和最小值。例如:
List<String> list = Arrays.asList("one", "two", "three"); Stream<String> stream = list.stream(); // 得到原始类型流 IntStream intStream = stream.mapToInt(String::length); IntSummaryStatistics statistics = intStream.summaryStatistics(); System.out.println("avg = " + statistics.getAverage()); System.out.println("count = " + statistics.getCount()); System.out.println("max = " + statistics.getMax()); System.out.println("min = " + statistics.getMin()); System.out.println("sum = " + statistics.getSum()); //输出: //avg = 3.6666666666666665 //count = 3 //max = 5 //min = 3 //sum = 11
注意:Random 类现在提供了 ints、longs 和 doubles 这些方法,用来返回包含随机数字的原始类型流。例如:
Random random = new Random(); // 生成10个 0~100 随机整数 int[] randoms = random.ints(10, 0, 100).toArray(); System.out.println(Arrays.toString(randoms)); //输出: //[41, 73, 58, 46, 95, 3, 29, 72, 57, 48]