中间操作无状态
filter
就是过滤条件, boolean Pridicate< T > 根据返回值类型来确定是否过滤这一条属性
List<Integer> list = Arrays.asList(1, 2,3, 4, 5, 6, 7, 8, 9);
list.stream().filter(x->x>=5).forEach(System.out::print);
map
map——接收 Lambda , 将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add("python");
list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);
结果:
HELLO
WORLD
JAVA
PYTHON
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
nums.stream().map(n -> n * n).collect(Collectors.toList()).forEach(System.out::println);
1.抽取对象的code作为key,name作为value转化为map集合
再加一个user对象
User user2 = new User("pg3","北京","1");
list1.add(user2)
(k1,k2)->k2 意思是遇到相同的key时取第二个值 (k1,k2)->k1 意思是遇到相同的key时取第一个值
Map<String, String> collect = list1.stream().
collect(Collectors.toMap(User::getAddress, User::getName, (k1, k2) -> k2));
System.out.println(collect);
执行结果为:
map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap。
flatMap
// 将集合中的字符串中单词提取出来,不考虑特殊字符
List<String> words = Arrays.asList("hello c++", "hello java", "hello python");
words.stream()
// 将单词按照空格切合,返回Stream<String[]>类型的数据
.map(word -> word.split(" "))
// 将Stream<String[]>转换为Stream<String>
.flatMap(Arrays::stream)
// 去重
.distinct()
.collect(Collectors.toList()).forEach(System.out::println);
hello
c++
java
python
案例:对给定单词列表 [“Hello”,“World”],你想返回列表[“H”,“e”,“l”,“o”,“W”,“r”,“d”]
String[] words = new String[]{"Hello","World"};
List<String[]> a = Arrays.stream(words)
.map(word -> word.split(""))
.distinct()
.collect(Collectors.toList());
a.forEach(System.out::print);
代码输出为:[Ljava.lang.String;@12edcd21[Ljava.lang.String;@34c45dca (返回一个包含两个String[]的list)
这个实现方式是由问题的,传递给map方法的lambda为每个单词生成了一个String。因此,map返回的流实际上是Stream<String[]> 类型的。你真正想要的是用Stream来表示一个字符串。
下方图是上方代码stream的运行流程
第二种方式:flatMap(对流扁平化处理)
String[] words = new String[]{"Hello","World"};
List<String> a = Arrays.stream(words)
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
a.forEach(System.out::print);
输出:
使用flatMap方法的效果是,各个数组并不是分别映射一个流,而是映射成流的内容,所有使用map(Array::stream)时生成的单个流被合并起来,即扁平化为一个流。
下图是运用flatMap的stream运行流程,
peek
Stream stream = Stream.of("one", "two", "three","four");
stream.peek(System.out::println);//但实际上没有任何输出
peek主要被用在debug用途,我们看下debug用途的使用:
Stream.of("one", "two", "three","four").filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
//上面的例子输出:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
上面的例子我们输出了stream的中间值,方便我们的调试。
为什么只作为debug使用呢?我们再看一个例子:
Stream.of("one", "two", "three","four").peek(u -> u.toUpperCase())
.forEach(System.out::println);
上面的例子我们使用peek将element转换成为upper case。然后输出:
one
two
three
four
可以看到stream中的元素并没有被转换成大写格式。
再看一个map的对比:
Stream.of("one", "two", "three","four").map(u -> u.toUpperCase())
.forEach(System.out::println);
输出:
ONE
TWO
THREE
FOUR
当然 peek也有例外 ,假如我们Stream里面是一个对象会怎么样?
@Data
@AllArgsConstructor
static class User{
private String name;
}
List userList=Stream.of(new User("a"),new User("b"),new User("c")).peek(u->u.setName("kkk")).collect(Collectors.toList());
log.info("{}",userList);
输出结果:
10:25:59.784 [main] INFO com.flydean.PeekUsage - [PeekUsage.User(name=kkk), PeekUsage.User(name=kkk), PeekUsage.User(name=kkk)]
上面得就是set 的典型的操作
为什么peek和map有这样的区别呢?
参数为Consumer<? super T> action
Stream peek(Consumer super T> action)
Stream map(Function super T, ? extends R> mapper);
peek接收一个Consumer,而map接收一个Function。
Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。
这就是为什么peek String不会发生变化而peek Object会发送变化的原因。
寄语:
|