🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### [随机数流](https://lingcoder.gitee.io/onjava8/#/book/14-Streams?id=%e9%9a%8f%e6%9c%ba%e6%95%b0%e6%b5%81) `Random`类被一组生成流的方法增强了。代码示例: ~~~ // streams/RandomGenerators.java import java.util.*; import java.util.stream.*; public class RandomGenerators { public static <T> void show(Stream<T> stream) { stream .limit(4) .forEach(System.out::println); System.out.println("++++++++"); } public static void main(String[] args) { Random rand = new Random(47); show(rand.ints().boxed()); show(rand.longs().boxed()); show(rand.doubles().boxed()); // 控制上限和下限: show(rand.ints(10, 20).boxed()); show(rand.longs(50, 100).boxed()); show(rand.doubles(20, 30).boxed()); // 控制流大小: show(rand.ints(2).boxed()); show(rand.longs(2).boxed()); show(rand.doubles(2).boxed()); // 控制流的大小和界限 show(rand.ints(3, 3, 9).boxed()); show(rand.longs(3, 12, 22).boxed()); show(rand.doubles(3, 11.5, 12.3).boxed()); } } ~~~ 输出结果: ~~~ -1172028779 1717241110 -2014573909 229403722 ++++++++ 2955289354441303771 3476817843704654257 -8917117694134521474 4941259272818818752 ++++++++ 0.2613610344283964 0.0508673570556899 0.8037155449603999 0.7620665811558285 ++++++++ 16 10 11 12 ++++++++ 65 99 54 58 ++++++++ 29.86777681078574 24.83968447804611 20.09247112332014 24.046793846338723 ++++++++ 1169976606 1947946283 ++++++++ 2970202997824602425 -2325326920272830366 ++++++++ 0.7024254510631527 0.6648552384607359 ++++++++ 6 7 7 ++++++++ 17 12 20 ++++++++ 12.27872414236691 11.732085449736195 12.196509449817267 ++++++++ ~~~ 为了消除冗余代码,我创建了一个泛型方法`show(Stream<T> stream)`(在讲解泛型之前就使用这个特性,确实有点作弊,但是回报是值得的)。类型参数`T`可以是任何类型,所以这个方法对**Integer**、**Long**和**Double**类型都生效。但是**Random**类只能生成基本类型**int**,**long**,**double**的流。幸运的是,`boxed()`流操作将会自动地把基本类型包装成为对应的装箱类型,从而使得`show()`能够接受流。 我们可以使用**Random**为任意对象集合创建**Supplier**。如下是一个文本文件提供字符串对象的例子。 Cheese.dat 文件内容: ~~~ // streams/Cheese.dat Not much of a cheese shop really, is it? Finest in the district, sir. And what leads you to that conclusion? Well, it's so clean. It's certainly uncontaminated by cheese. ~~~ 我们通过**File**类将 Cheese.dat 文件的所有行读取到`List<String>`中。代码示例: ~~~ // streams/RandomWords.java import java.util.*; import java.util.stream.*; import java.util.function.*; import java.io.*; import java.nio.file.*; public class RandomWords implements Supplier<String> { List<String> words = new ArrayList<>(); Random rand = new Random(47); RandomWords(String fname) throws IOException { List<String> lines = Files.readAllLines(Paths.get(fname)); // 略过第一行 for (String line : lines.subList(1, lines.size())) { for (String word : line.split("[ .?,]+")) words.add(word.toLowerCase()); } } public String get() { return words.get(rand.nextInt(words.size())); } @Override public String toString() { return words.stream() .collect(Collectors.joining(" ")); } public static void main(String[] args) throws Exception { System.out.println( Stream.generate(new RandomWords("Cheese.dat")) .limit(10) .collect(Collectors.joining(" "))); } } ~~~ 输出结果: ~~~ it shop sir the much cheese by conclusion district is ~~~ 在这里可以看到`split()`更复杂的运用。在构造器里,每一行都被`split()`通过方括号内的空格或其它标点符号分割。在方括号后面的`+`表示`+`前面的东西可以出现一次或者多次。 你会发现构造函数使用命令式编程(外部迭代)进行循环。在以后的例子中,你会看到我们是如何去除命令式编程的使用。这种旧的形式虽不是特别糟糕,但使用流会让人感觉更好。 在`toString()`和`main()`方法中你看到了`collect()`操作,它根据参数来结合所有的流元素。当你用`Collectors.joining()`作为`collect()`的参数时,将得到一个`String`类型的结果,该结果是流中的所有元素被`joining()`的参数隔开。还有很多不同的`Collectors`用于产生不同的结果。 在主方法中,我们提前看到了**Stream.**`generate()`的用法,它可以把任意`Supplier<T>`用于生成`T`类型的流。