转载

Stream

从迭代到Stream操作

流表面上看起来与集合相似,允许你转换和检索数据。然而,两者却有显著的不同:

  • 流不储存元素。元素储存在底层的集合或者按需生成。
  • 流操作不改变它们的源数据。
  • 如果可能的话,Stream操作就是延迟执行的。这意味着直到需要结果时,方法才会执行。

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

转换成流

  • 面对集合,使用Collection接口的stream方法
  • 面对数组,使用Stream.of();

创建一个不包含任何元素的Stream

Stream<String> silence = Stream.empty()

创建无限Stream的静态方法

  • 创建一个常量值,使用generate()
//创建一个常量值
Stream.generate(() -> "Echo");
//创建一个随机数
Stream.generate(Math :: random);
  • 创建一个例如0 1 2 3...... 这样的无穷序列,可使用iterate(),
Stream.iterate(BigInteger.ZERO , n->n.add(BigInteger.ONE));

转换流

filter

转换成一个匹配一定条件的新流。

stream().filter(w -> w.length() > 12);

map

将一个流中的值进行某种形式的转换。

比如:将所有单词转换成小写形式

List<String> words = ...
Stream<String> lowercaseWords = words.stream().map(String :: toLowerCase);

flatMap

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));

提取子流和组合流

stream.limit()

返回一个包含n个元素的新流(如果原始流的长度小于n,则会返回原始流)。

Stream.generate(Math :: random).limit(100);

stream.skip()

它会丢弃前n个元素。

Stream.of("a","b","c").skip(1);

stream.takeWhile(predicate)

从流接收所有元素,当predicate为true时,停止接收。

Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())  
        .forEach(System.out::print);

stream.dropWhile()

当条件判断为true时,丢弃元素。该方法生成一个流,该流包含了所以条件判断为false的元素。

Stream.of("a","b","c","","e","f").dropWhile(s->s.isEmpty()).forEach(System.out::print);

Stream.concat()

可以将两个流连接起来。

Stream.concat(Stream.of("a","b","c"),Stream.of("e","f")).forEach(System.out::print);

其他流转换

distinct()

去重

sorted()

排序

Stream.of("gg","b","c","","e","f").sorted(Comparator.comparing(String::length).reversed());

peek()

生成一个与原先流一样有着相同元素的新流,但是每当检索一个元素时函数就调用一次。对调试来说非常方便。

如果你想知道流的流水线之特定地方发生了什么,则添加如下代码:

.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类型

有效的使用Optional的关键在于:使用一个要么如果值不存在,产生另一个替代值; 要么如果值存在,使用该值的方法。

如果值不存在,产生另一个替代值

  • 使用一个默认值
String result = optionalString.orElse("");
  • 调用代码来计算默认值
String result = optionalString.orElseGet(() -> System.getProperty("myapp.default"));
  • 抛出一个异常
String result = optionalString.orElseThrow(IllegalStateException :: new);

当值存在,使用它

  • 使用ifPresent(),如果Optional值存在的话,则它会被传递给函数。当使用它给函数传递Option值时,函数返回值会丢失。
optionalValue.ifPresent(v -> Process v);
//例如:
 List<String> list = new ArrayList<>();  
 optionalValue.ifPresent(v ->list.add(v));
 //或者
 optionalValue.ifPresent(list::add);
  • ifPresentOrElse(),当Optional有值时执行某个任务,没有值时执行另外一个任务。
optionalValue.ifPresentOrElse(v -> Process v, ()->执行其他任务);
  • 使用map(),可以处理函数结果。
//如果存在一个值,则返回一个可选的,描述将给定的映射函数应用于该值的结果(就像通过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流,而不用进行包装。

原文  https://segmentfault.com/a/1190000021480379
正文到此结束
Loading...