流表面上看起来与集合相似,允许你转换和检索数据。然而,两者却有显著的不同:
Stream遵循"做什么,而不是怎么去做"的原则。
比如,例子:计算集合中长度大于3的总数
String str = "aaa,cccc,dddd,eeee,ffff"; List<String> words = List.of(str.split(",")); //迭代 int count = 0; for (String w : words) { if(w.length() > 3) count++; } //使用流时,只需要描述需要做的事情 long count = words.stream().filter(w -> w.length() >3).count(); //采用并行流,并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。并行流只有对大型数据集才能显示出优势。 long count = words.parallelStream().filter(w -> w.length() >3).count(); //如果words对象已经是流,可以使用parallel()并行执行。 long count = words.parallel().filter(w -> w.length() >3).count();
转换成流
创建一个不包含任何元素的Stream
Stream<String> silence = Stream.empty()
创建无限Stream的静态方法
//创建一个常量值 Stream.generate(() -> "Echo"); //创建一个随机数 Stream.generate(Math :: random);
Stream.iterate(BigInteger.ZERO , n->n.add(BigInteger.ONE));
转换成一个匹配一定条件的新流。
stream().filter(w -> w.length() > 12);
将一个流中的值进行某种形式的转换。
比如:将所有单词转换成小写形式
List<String> words = ... Stream<String> lowercaseWords = words.stream().map(String :: toLowerCase);
public static Stream<String> codePoints(String s){ List<String> result = new ArrayList<>(); int i =0; while (i < s.length()) { int j = s.offsetByCodePoints(i, 1); result.add(s.substring(i,j)); i = j; } return result.stream(); } String[] strWords = {"words"}; //将得到一个包含多个流的流 Stream<Stream<String>> result1 = Stream.of(strWords).map(w -> codePoints(w)); //将得到一个只包含字符串的流 Stream<String> result2 = Stream.of(strWords).flatMap(w -> codePoints(w));
返回一个包含n个元素的新流(如果原始流的长度小于n,则会返回原始流)。
Stream.generate(Math :: random).limit(100);
它会丢弃前n个元素。
Stream.of("a","b","c").skip(1);
从流接收所有元素,当predicate为true时,停止接收。
Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty()) .forEach(System.out::print);
当条件判断为true时,丢弃元素。该方法生成一个流,该流包含了所以条件判断为false的元素。
Stream.of("a","b","c","","e","f").dropWhile(s->s.isEmpty()).forEach(System.out::print);
可以将两个流连接起来。
Stream.concat(Stream.of("a","b","c"),Stream.of("e","f")).forEach(System.out::print);
去重
排序
Stream.of("gg","b","c","","e","f").sorted(Comparator.comparing(String::length).reversed());
生成一个与原先流一样有着相同元素的新流,但是每当检索一个元素时函数就调用一次。对调试来说非常方便。
如果你想知道流的流水线之特定地方发生了什么,则添加如下代码:
.peek(x ->{ return;}) 并在第二行设置断点。
从流数据中获得答案,归约是终止操作,比如count()、max()、min()等。
//大部分方法会返回一个Optional<T>类型的值,它可能会封装返回值,也可能表示没有返回(当流为空时)。 String str = "b,o,a,t"; List<String> words = List.of(str.split(",")); Optional<String> largest = words.stream().max(String::compareToIgnoreCase); System.out.println(largest.orElse(""));
有效的使用Optional的关键在于:使用一个要么如果值不存在,产生另一个替代值; 要么如果值存在,使用该值的方法。
如果值不存在,产生另一个替代值
String result = optionalString.orElse("");
String result = optionalString.orElseGet(() -> System.getProperty("myapp.default"));
String result = optionalString.orElseThrow(IllegalStateException :: new);
当值存在,使用它
optionalValue.ifPresent(v -> Process v); //例如: List<String> list = new ArrayList<>(); optionalValue.ifPresent(v ->list.add(v)); //或者 optionalValue.ifPresent(list::add);
optionalValue.ifPresentOrElse(v -> Process v, ()->执行其他任务);
//如果存在一个值,则返回一个可选的,描述将给定的映射函数应用于该值的结果(就像通过ofNullable一样),否则返回一个空的可选的。如果映射函数返回空结果,则此方法返回空的可选结果。 Optional<Boolean> data = optionalValue.map(list::add);
遍历流
stream.forEach(System.out :: printIn);
将结果收集到一个数据结构中
String[] result = stream.toArray(String[] :: new); //stream.toArray()会返回Object[]类型,可以将类型传递给数组的构造函数,来获取相应类型的数组
//收集到list中 List<String> result = stream.collect(Collectors.toList()); //收集到set中 Set<String> result = stream.collect(Collectors.toSet()); //收集到哪种集合中 HashSet<String> result = stream.collect(Collectors.toCollection(HashSet::new)); //如果你想将流中的所有字符串拼接并收集起来,并且流包含字符串以外的对象 String result = stream.map(Object::toString).collect(Collectors.joining()); //也可以在元素之间插入分隔符 String result = stream.map(Object::toString).collect(Collectors.joining(",")); //收集到Map中 Stream<String> stream = Stream.of("aaa", "bbbb", "ccccc"); Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String:: length)); //数据视图结果:{aaa=3, ccccc=5, bbbb=4} //Function.identity()返回一个输出跟输入一样的Lambda表达式对象,等价于形如`t -> t`形式的Lambda表达式
Stream类库提供了IntStream、LongStream和DoubleStream类型,分别表示原始int流、原始long流和原始double流,而不用进行包装。