之前也写过很多篇关于Java8使用的文章了,但是回顾一下,好像还没介绍过Java8 Stream的flatMap操作,昨天刚好在工作中遇到一个场景,发现flatMap简直太方便了,这里总结一下flatMap的常规使用。附带讲一下,使用Java8实现集合的并、交、差操作,其实之前也讲过一种使用Guava的实现方式,具体请参考Guava集合工具
-
flatMap
首先看一下一种场景,存在一个Map<Integer, ListContainer>,ListContainer中存在一个List<AClass>成员变量。有这样一个需求,讲Map中values中所有的List<AClass>组合成一个List<AClass>。我们也许会这样操作:
List<AClass> resultAClassList = ();
for (ListContainer tmp : ()){
(());
}
这还是只存在一层List的情况,如果存在多层,for还需要嵌套,使用起来很不方便。后来查到,Java8 Stream的flatMap操作可以很好地适用这种场景,首先看一下flatMap方法定义:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
参数是一个Function函数式接口,提供T到Stram的转换。其实参考方法实现,flatMap就是将Function转化后的Stram合并成一个Stream。下面看一下使用示例,完成上述相同的功能:
@Test
public void mergeMapValuesTest(){
Map<Integer, ListContainer> map = ();
List<AClass> aClassList1 = ();
AClass aClass = new AClass(1, "zhuoli1", "haha1");
(aClass);
(new AClass(2, "zhuoli2", "haha2"));
(new AClass(3, "zhuoli3", "haha3"));
List<AClass> aClassList2 = ();
(aClass);
(new AClass(5, "zhuoli5", "haha5"));
(new AClass(6, "zhuoli6", "haha6"));
/*交集*/
/*[AClass(id=1, name=zhuoli1, description=haha1)]*/
List<AClass> intersectResult = ().filter(aClassList2::contains).collect(());
(intersectResult);
/*并集*/
List<AClass> unionResult = (aClassList1, aClassList2).flatMap(Collection::stream).distinct().collect(());
assertEquals((), 5);
(unionResult);
/*差集*/
/*[AClass(id=2, name=zhuoli2, description=haha2), AClass(id=3, name=zhuoli3, description=haha3)]*/
List<AClass> differenceResult = ().filter(x -> !(x)).collect(());
(differenceResult);
(1, new ListContainer(aClassList1));
(2, new ListContainer(aClassList2));
/*合并多个list*/
List<AClass> aClassListResult = ().stream().flatMap(listContainer -> ().stream()).collect(());
/*注意跟并集的区别*/
assertEquals((), 6);
(aClassListResult);
}
分享一个flatMap的复杂操作,实现List<Data1>和List<Data2>根据Id进行连接,将连接结果输出为一个List<OutputData>:
@Data
@AllArgsConstructor
public class Data1 {
private int id;
private String name;
private int amount;
}
@Data
@AllArgsConstructor
public class Data2 {
private int id;
private String name;
private String type;
}
@Data
@AllArgsConstructor
public class OutputData {
private int id;
private String name;
private String type;
private int amount;
}
@Test
public void intersectByKeyTest(){
List<Data2> listOfData2 = new ArrayList<Data2>();
(new Data2(10501, "JOE" , "Type1"));
(new Data2(10603, "SAL" , "Type5"));
(new Data2(40514, "PETER", "Type4"));
(new Data2(59562, "JIM" , "Type2"));
(new Data2(29415, "BOB" , "Type1"));
(new Data2(61812, "JOE" , "Type9"));
(new Data2(98432, "JOE" , "Type7"));
(new Data2(62556, "JEFF" , "Type1"));
(new Data2(10599, "TOM" , "Type4"));
List<Data1> listOfData1 = new ArrayList<Data1>();
(new Data1(10501, "JOE" ,3000000));
(new Data1(10603, "SAL" ,6225000));
(new Data1(40514, "PETER" ,2005000));
(new Data1(59562, "JIM" ,3000000));
(new Data1(29415, "BOB" ,3000000));
List<OutputData> result = ()
.flatMap(x -> ()
.filter(y -> () == ())
.map(y -> new OutputData((), (), (), ())))
.collect(());
(result);
/*difference by key*/
List<Data1> data1IntersectResult = ().filter(data1 -> ().map(Data2::getId).collect(()).contains(())).collect(());
(data1IntersectResult);
}
-
faltMapToInt
首先看一下flatMapToInt方法定义:
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
跟flatMap不同的是,参数Function函数式接口提供由T到IntStream的转化,方法返回值是IntStream。
@Test
public void flatMapToIntTest() {
List<List<String>> listOfLists = (
("1", "2"),
("5", "6"),
("3", "4")
);
IntStream intStream =
()
.flatMapToInt(childList ->
()
.mapToInt(Integer::new));
int sum = (::println).sum();
("sum: " + sum);
}
Stream接口中还存在类似的方法flatMapToDouble、flatMapToLong,使用方法跟flatMapToInt是一样的,这里就不多介绍了,仅罗列一下方法定义:
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
示例代码:码云 – 卓立 – Java8 flatMap示例