前言
不同于java7直白分段且累赘的描述,java8对函数式接口、lambda表达式、stream流等的综合运用使得业务逻辑清晰连贯,一步到位。尤其适用于数据流处理(例如对数据集合的过滤,转换,分组,统计等)。
1 lambda表达式和函数接口
1.1 lambda表达式
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//方式1
list.forEach( s -> System.out.println(s) );
//方式2
list.forEach( s -> {System.out.println(s)} );
直观地理解,lambda表达式为某个函数式接口的完整实现。由两部分组成,入参和业务逻辑。其中入参为item,方法的实现逻辑为System.out.println(item)。
1.2 函数式接口
函数式接口是指有且只有一个抽象方法(Object类中的方法不包括在内)的接口
Consumer
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
Supplier
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Predicate
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Function
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
1.3 lambda表达式和函数式接口的配合使用
- 函数式接口Consumer的lambda表达式实现示例
Consumer<String> consumer = (s) -> System.out.println(s);
- lambda表达式的使用示例
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//方式1
list.forEach( consumer );
//方式2
list.forEach((s) -> System.out.println(s));
理解:item就是函数式接口Consumer的accept()方法的入参,而System.out.println(item)则是accept函数的具体实现逻辑。
- 小结 这里,lambda表达式能作为函数的入参,实则是因为lambda表达式本身是一个对象,相当于给函数入参了一个对象引用。其次为什么可以忽略入参类型呢,也就是为什么可以只传个表达式呢,那是因为函数的形参定义本就为确定的函数式接口(例如例子中的Consumer)。
1.4 lambda表达式的在forEach中的使用
- forEach方法是Iterable接口中的默认方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
forEach方法接收一个Consumer(函数式接口)对象引用作为入参,并在方法中调用Consumer的accept()方法,即执行了lambda表达式的业务逻辑。
- list集合调用forEach执行lambda表达式,打印集合中的所有元素
public class ForEachTest {
@Test
public void test_Consumer() {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
list.forEach( item -> System.out.println(item) );
}
}
List接口继承了Collection接口,而Collection接口则继承了Iterable接口,所以list可以调用forEach方法。
2 stream流
分组groupingBy,后续归约操作
package java8;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors;
public class StreamDemo { @Data @NoArgsConstructor @AllArgsConstructor static class Point { String name; String cityCode; long timeSpan; }
@Data
@NoArgsConstructor
@AllArgsConstructor
static
class Demo{
String name;
String cityCode;
long sumTimeSpan;
}
public static void main(String[] args) {
List<Point> list = new ArrayList<>();
Point p1 = new Point("广州","440100", 20);
Point p2 = new Point("广州","440100", 30);
Point p3 = new Point("梅州","440200", 40);
Point p4 = new Point("茂名","440300", 50);
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
Map<String, Demo> map1 = list.parallelStream().collect(Collectors.groupingBy(
Point::getName,
Collectors.collectingAndThen(
Collectors.reducing( (a, b) -> new Point(a.getName(), a.getCityCode(), a.getTimeSpan() + b.getTimeSpan()) ),
item -> {
Point point = item.get();
return new Demo(point.getName(), point.getCityCode(), point.getTimeSpan());
}
)
));
Map<String, Demo> map2 = list.parallelStream().collect(Collectors.groupingBy(
Point::getName,
Collectors.collectingAndThen(
Collectors.reducing(
new Point("广州","440100", 0),
(a, b) -> new Point(b.getName(), b.getCityCode(), a.getTimeSpan() + b.getTimeSpan())
),
point -> {
return new Demo(point.getName(), point.getCityCode(), point.getTimeSpan());
}
)
));
Map<String, Demo> map3 = list.parallelStream().collect(Collectors.groupingBy(
Point::getName,
Collectors.collectingAndThen(
Collectors.reducing(
new Demo("广州","440100", 0),
item -> new Demo(item.getName(), item.getCityCode(), item.getTimeSpan()),
(a, b) -> new Demo(a.getName(), b.getCityCode(), a.getSumTimeSpan() + b.getSumTimeSpan())
),
demo -> demo
)
));
System.out.println("-----------map1");
map1.forEach((k,v) -> System.out.println("k = " + k + ", v = " + v));
System.out.println("-----------map2");
map2.forEach((k,v) -> System.out.println("k = " + k + ", v = " + v));
System.out.println("-----------map3");
map3.forEach((k,v) -> System.out.println("k = " + k + ", v = " + v));
}
}
分组groupingBy
groupingBy后续操作 示例1
@Data
public class StreamDemo {
@Data
class User{
String name;
String cityCode;
}
public void main(String[] args) {
List<User> list = new ArrayList<>();
//根据人员所在城市分组,并对分组后的人员列表翻译其对应城市的中文名
Map<String, List<User>> map = list.parallelStream().collect(Collectors.groupingBy(
User::getCityCode,
Collectors.mapping(
item -> {
User user = new User();
//翻译中文名
//...
return user;
},
Collectors.toList()
)
));
}
}
groupingBy后续操作 示例2
@Data
public class StreamDemo {
@Data
class User{
String name;
String cityCode;
}
public void main(String[] args) {
List<User> list = new ArrayList<>();
//根据人员所在城市分组,并统计分组后每组各自的人员列表总数
Map<String, Long> map = list.parallelStream().collect(Collectors.groupingBy(
User::getCityCode,
Collectors.counting()
));
}
}