java-java8-写出优雅简洁的代码

747 阅读3分钟

前言

不同于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()
        ));
    }
}

串行和并发

优劣比对

3 DateTime

DateTime的使用示例

传统的Date工具

excel中的时间戳转换

oracle等sql中的时间戳转换