从迭代到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();
创建Stream
转换成流
- 面对集合,使用Collection接口的stream方法
- 面对数组,使用Stream.of();
创建一个不包含任何元素的StreamStream<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流,而不用进行包装。
并行流
使用并行流能够自动将大型数据集上的操作并行化。只有当你对已经存在于内存中的大量数据进行持续计算时,才需要使用并行流,并且要确保远离对共享对象的不安全修改。
String str = "aaa,cccc,dddd,eeee,ffff";
List<String> words = List.of(str.split(","));
long count = words.parallelStream().filter(w -> w.length() >3).count();
//如果words对象已经是流,可以使用parallel()并行执行。
long count = words.parallel().filter(w -> w.length() >3).count();
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。