ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### [集合](https://lingcoder.gitee.io/onjava8/#/book/14-Streams?id=%e9%9b%86%e5%90%88) * `collect(Collector)`:使用**Collector**收集流元素到结果集合中。 * `collect(Supplier, BiConsumer, BiConsumer)`:同上,第一个参数**Supplier**创建了一个新的结果集合,第二个参数**BiConsumer**将下一个元素收集到结果集合中,第三个参数**BiConsumer**用于将两个结果集合合并起来。 在这里我们只是简单介绍了几个**Collectors**的运用示例。实际上,它还有一些非常复杂的操作实现,可通过查看`java.util.stream.Collectors`的 API 文档了解。例如,我们可以将元素收集到任意一种特定的集合中。 假设我们现在为了保证元素有序,将元素存储在**TreeSet**中。**Collectors**里面没有特定的`toTreeSet()`,但是我们可以通过将集合的构造函数引用传递给`Collectors.toCollection()`,从而构建任何类型的集合。下面我们来将一个文件中的单词收集到**TreeSet**集合中。代码示例: ~~~ // streams/TreeSetOfWords.java import java.util.*; import java.nio.file.*; import java.util.stream.*; public class TreeSetOfWords { public static void main(String[] args) throws Exception { Set<String> words2 = Files.lines(Paths.get("TreeSetOfWords.java")) .flatMap(s -> Arrays.stream(s.split("\\W+"))) .filter(s -> !s.matches("\\d+")) // No numbers .map(String::trim) .filter(s -> s.length() > 2) .limit(100) .collect(Collectors.toCollection(TreeSet::new)); System.out.println(words2); } } ~~~ 输出结果: ~~~ [Arrays, Collectors, Exception, Files, Output, Paths, Set, String, System, TreeSet, TreeSetOfWords, args, class, collect, file, filter, flatMap, get, import, java, length, limit, lines, main, map, matches, new, nio, numbers, out, println, public, split, static, stream, streams, throws, toCollection, trim, util, void, words2] ~~~ **Files.**`lines()`打开**Path**并将其转换成为由行组成的流。下一行代码以一个或多个非单词字符(`\\W+`)为分界,对每一行进行分割,结果是产生一个数组,然后使用**Arrays.**`stream()`将数组转化成为流,最后`flatMap()`将各行形成的多个单词流,扁平映射为一个单词流。使用`matches(\\d+)`查找并移除全部是数字的字符串(注意,`words2`是通过的)。然后用**String.**`trim()`去除单词两边的空白,`filter()`过滤所有长度小于3的单词,并只获取前100个单词,最后将其保存到**TreeSet**中。 我们也可以在流中生成**Map**。代码示例: ~~~ // streams/MapCollector.java import java.util.*; import java.util.stream.*; class Pair { public final Character c; public final Integer i; Pair(Character c, Integer i) { this.c = c; this.i = i; } public Character getC() { return c; } public Integer getI() { return i; } @Override public String toString() { return "Pair(" + c + ", " + i + ")"; } } class RandomPair { Random rand = new Random(47); // An infinite iterator of random capital letters: Iterator<Character> capChars = rand.ints(65,91) .mapToObj(i -> (char)i) .iterator(); public Stream<Pair> stream() { return rand.ints(100, 1000).distinct() .mapToObj(i -> new Pair(capChars.next(), i)); } } public class MapCollector { public static void main(String[] args) { Map<Integer, Character> map = new RandomPair().stream() .limit(8) .collect( Collectors.toMap(Pair::getI, Pair::getC)); System.out.println(map); } } ~~~ 输出结果: ~~~ {688=W, 309=C, 293=B, 761=N, 858=N, 668=G, 622=F, 751=N} ~~~ **Pair**只是一个基础的数据对象。**RandomPair**创建了随机生成的**Pair**对象流。在 Java 中,我们不能直接以某种方式组合两个流。所以我创建了一个整数流,并且使用`mapToObj()`将整数流转化成为**Pair**流。**capChars**的随机大写字母迭代器创建了流,然后`next()`让我们可以在`stream()`中使用这个流。就我所知,这是将多个流组合成新的对象流的唯一方法。 在这里,我们只使用最简单形式的`Collectors.toMap()`,这个方法只需要两个从流中获取键和值的函数。还有其他重载形式,其中一种当是键发生冲突时,使用一个函数来处理冲突。 大多数情况下,`java.util.stream.Collectors`中预设的**Collector**就能满足我们的要求。除此之外,你还可以使用第二种形式的`collect()`。 我把它留作更高级的练习,下例给出基本用法: ~~~ // streams/SpecialCollector.java import java.util.*; import java.util.stream.*; public class SpecialCollector { public static void main(String[] args) throws Exception { ArrayList<String> words = FileToWords.stream("Cheese.dat") .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); words.stream() .filter(s -> s.equals("cheese")) .forEach(System.out::println); } } ~~~ 输出结果: ~~~ cheese cheese ~~~ 在这里,**ArrayList**的方法已经做了你所需要的操作,但更有可能的是,如果你必须使用这种形式的`collect()`,就要自己创建特定的定义。