Java8 特性-stream①
-
集合与流的区别:
- 集合好比看电影时下载到本地,然后本地观看;流相当于在线观看;集合是将整部电影都存储到本地,是一个容器内完整的数据;而流是更大更广义,我们只浏览流到本地的数据;
-
读Stream组成结构:
- A:数据源;B:中间处理;C:终结方法;
- A:数据的起源,Stream流的来源,所有可以转换为Stream流的对象或是容器,如图1-1;
- B:Stream流的处理过程,但是不会改变流,如图1-2;
- C:将Stream流转换为另一种对象或是容器,如图1-3;
- 比如获取一个文件或者读取List,Set等都可以转换为Stream流,均是数据源的部分;filter,map等均属于中间处理过程,处理后流仍是流,对象不会被改变;forEach,collect等均属于终结方法,forEach没有返回值,而collect将原来的流归纳为新的对象;
图 1-1
图1-2
图1-3
-
创建Stream:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
Stream<String> stream = Stream.of( "111", "2222", "333", "44444", null, "777", "2222" );
-
转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
-
collect方法,可以将 Stream转换为想要的容器,比如Map、Set、List等;
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","2222" );
Set<String> collect = list.stream().filter( f -> f != null ).filter( (String f) -> !f.equals( "111" ) ).collect( Collectors.toList() ).stream().filter( (String f) -> !f.equals( "777" ) ).collect( Collectors.toSet() );
- filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;
- filter过滤函数是可以重复调用的;
- 案例中过滤掉空值,过滤点String 为111和777的值
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","2222" );
Set<String> collect = list.stream().filter( f -> f != null ).filter( (String f) -> !f.equals( "111" ) ).collect( Collectors.toList() ).stream().filter( (String f) -> !f.equals( "777" ) ).collect( Collectors.toSet() );
collect.forEach( f-> System.out.println(f) );
- 测试结果
- map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型;
- 这里map可以理解为对象处理器,针对当前对象处理;
- 如果只有一行代码,默认返回该代码结果,省略return语句,多行代码需要在->后加方法体{},并且加return语句;
- flatMap将对象平行化处理;
- 一段代码更好的理解 flatMap 与 map 的区别
List<String> list = new ArrayList<>();
list.add("Holle world and you world");
list.add("爱 要 坚持 且 持久");
/**
* Stream.flatMap ,平由化(把内部元素当作一个原处理)
* 八个 string对象
* 针对对象内细微元素
* 必须返回 Stream 对象
*/
List<String> result1 = list.stream().flatMap(t -> Stream.of(t.split(" ")))
.collect(Collectors.toList());
System.out.println(result1.size());
result1.forEach(System.out::println);
System.out.println("------------------------------------------------------------------------------");
/**
* 这个例子可以当作stream进行了两次for循环,快速梳理数据
*/
String[] words1 = new String[]{"Hello","World"};
String[] words2 = new String[]{"ccd","World"};
List<String> a = Arrays.stream(words1)
.flatMap(w1 ->Arrays.stream(words2).filter(w2 -> w1.equals(w2)))
.distinct()
.collect(Collectors.toList());
a.forEach(System.out::println);
/**
* Stream.map , 原生态 (原生态处理)
* 两个 String[],集合内数据结构被改变了
* 针对对象
* 返回任意对象
*/
List<String[]> result2 = list.stream().map(t -> t.split(" "))
.collect(Collectors.toList());
System.out.println(result2.size());
result2.forEach(System.out::println);
System.out.println("------------------------------------------------------------------------------");
result2.stream().forEach( array ->{
System.out.println(array.length);
for (String s : array) {
System.out.println(s);
}
});
- mapToInt:
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","2222" );
int sum = list.stream().filter( o -> o != null ).mapToInt( (String m) -> Integer.valueOf( m ) ).sum();
System.out.println(sum);
测试结果:
- map:
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","2222" );
Set<Double> map = list.stream().filter( f -> f != null ).map( (String m) -> Double.valueOf( m ) ).collect( Collectors.toSet() );
map.forEach( s-> System.out.println(s) );
- 测试结果
- flatMap:
List<String> list = Arrays.asList( "张三", "李四", "王二" );
List<String> collect = list.stream().flatMap( m -> Stream.of( m.split( "" ) ) ).collect( Collectors.toList() );
collect.forEach( c-> System.out.println(c) );
- 测试结果:
- peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;换言而知,就是对元数据的修改,可以理解为一个没有返回值的方法,只可以对传进来的参数进行处理;
/**
* 在闭包中,所有属性默认是被 'final'修饰的
* 不能使用‘=’赋值,需要使用反射更改权限
* 如果是引用数据类型,可以get、add等封装方式直接赋值
* @param args
*/
public static void main(String[] args) {
String[] words1 = new String[]{"Hello","World"};
List<String> strings = Arrays.stream(words1)
.peek(s -> {
Class<String> stringClass = String.class;
try {
Field value = stringClass.getDeclaredField("value");
value.setAccessible(true);
value.set(s,"1".toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
})
.collect(Collectors.toList());
strings.forEach(System.out::println);
}
- limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
- skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
- 一般skip与limit连用,实现数据分片效果;skip跳过,limit分片数据;
- 对于distinct、count等聚合函数,可以理解为sql语句执行顺序那样,重要的是每一步要清晰的知道返回数据结果结构,读取这样的代码,貌似在读一篇文章;
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","sss" );
List<String> collect = list.stream().filter( f->f!=null ).distinct().skip( 2 ).limit( 3 ).collect( Collectors.toList() );
collect.forEach( System.out::println );
long count = list.stream().filter( f -> f != null ).distinct().skip( 2 ).limit( 3 ).count();
System.out.println("-----------------------------");
System.out.println(count);
测试结果:
- allMatch:是不是Stream中的所有元素都满足给定的匹配条件;
- anyMatch:Stream中是否部分元素满足匹配条件;
- findFirst: 返回Stream中的第一个元素,如果Stream为空,返回空Optional;
- noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件;
- max和min:使用给定的比较器(Operator),返回Stream中的最大|最小值;
- Operator是java8针对java.lang.NullPointerException问题提出的问题解决类;
- isPresent和ifPresent一般跟在比较断言后,提出对结果的猜想;
List<String> list = Arrays.asList( "111", "2222", "333", "44444",null,"777","2222" );
boolean b1 = list.stream().allMatch( m -> m == null );
System.out.println("是否全部为空" + b1);
boolean b2 = list.stream().anyMatch( m -> m == null );
System.out.println("是否部分为空" + b2);
boolean b3 = list.stream().noneMatch( m -> m == null );
System.out.println("是否全部不为空" + b3);
System.out.println("-----------------------------");
Optional<String> max = list.stream().filter( f -> f != null ).max( (m1, m2) -> Integer.valueOf( m1 ) - Integer.valueOf( m2 ) );
System.out.println(max.orElse( null ));
System.out.println("-----------------------------");
boolean present = list.stream().filter( f -> f != null ).max( (m1, m2) -> Integer.valueOf( m1 ) - Integer.valueOf( m2 ) ).isPresent();
System.out.println(present);
System.out.println("-----------------------------------");
list.stream().filter( f -> f != null ).max( (m1, m2) -> Integer.valueOf( m1 ) - Integer.valueOf( m2 ) ).ifPresent(s-> System.out.println(s));
测试结果:
- stream实现分组,返回结果Map,会将分组元素作为Map的Key,相对应的数据为Value
- 下面的例子是封装一个IPhone对象,对象给有颜色和重量两个属性,然后按颜色属性分组
- 利用分组实现转换Map
List<IPhone> list = getList();
Map<String, List<IPhone>> collect = list.stream().collect( Collectors.groupingBy( IPhone::getColor ) );
System.out.println(collect.toString());
- 测试结果为:
- stream转Map,这个需要指定Key和Value
List<IPhone> list = getList();
Map<String, Integer> collect = list.stream().collect( Collectors.toMap( IPhone::getColor, IPhone::getWeight ) );
System.out.println(collect.toString());
- 测试结果
- Reduce属于归纳性操作,看参数,第一个是U泛型,也就是输入类型的参数,最为初始值,第二个BiFunction,接收T,U参数,返回U类型参数,BinaryOperator接收U,U类型,并返回U类型;
- 无论是提前拟订好的容器,还是接到的返回值,其值对应的内存地址是一样的,也就是说对象(指针指向)是一样的,如果将提前拟订好的容器进行==或者equals对比其结果为true;
List<String> list = Arrays.asList( "张三", "李四", "王二" );
StringBuffer sb = new StringBuffer();//也可以在下方拟订匿名容器,这样就需要接返回值
System.out.println("输出初始化StringBuffer:"+sb);
StringBuffer reduce = list.stream().reduce( sb, (s1, s2) -> s1.append( s2 ), StringBuffer::append );
System.out.println(sb==reduce);
System.out.println(sb.equals( reduce ));
System.out.println(sb);
System.out.println("----------------------------");
ArrayList<String> reduceList = list.stream().reduce( new ArrayList<String>(), (a1, a2) -> {
a1.add( a2 );
return a1;
}, (b1, b2) -> {
b1.addAll( b2 );
return b1;
} );
reduceList.forEach( s-> System.out.println(s) );
System.out.println("---------------------------------");
HashMap<Object, Object> reduceMap = list.stream().reduce( new HashMap<>(), (m, m1) -> {
m.put( m1, m1 );
return m;
}, (m, m1) -> {
m.putAll( m1 );
return m;
} );
System.out.println(reduceMap.toString());
- 测试结果
方法很多,还需要理解与练习
chenyb 随笔记录,只为方便自己学习
2019-10-012