java8函数式编程

介绍

函数式编程最直接的表现就是将行为作为数据传递。使代码表达能力得到提升!在java8中主要有三个核心接口

  • 函数接口 (Function)
  • 流(Stream)
  • 聚合器(Collector)
函数接口
  1. 函数接口是行为的抽象
  2. 函数接口是数据转换器

在java.util.funciton包中定义了定义了四个最基础的函数接口

  1. Supplier<T> 数据提供器,可以提供 T 类型对象;无参的构造器,提供了 get 方法
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Supplier 类似工厂方法 example

public class SupplierTest {

    public static void main(String[] args) {
        Supplier<User> userSupplier = () -> new User();
        System.out.println(userSupplier.get());
        System.out.println(userSupplier.get());
    }

}

输出
lamdal.User@27bc2616
lamdal.User@3941a79c

可以看到每一次get都会产生一个新的对象
  1. Function<T, R> 数据转换器,接收一个 T 类型的对象,返回一个 R类型的对象; 单参数单返回值的行为接口;提供了 apply, compose, andThen, identity 方法
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

Funciton example

public class FunctionTest {

    public static void main(String[] args) {
        FunctionTest functionTest = new FunctionTest();
        System.out.println(functionTest.compute1(1, i -> i * 2, i -> i * i));
        System.out.println(functionTest.compute2(1, i -> i * 2, i -> i * i));

    }

    private int compute1(int i, Function<Integer, Integer> before, Function<Integer, Integer> after) {
        return after.compose(before).apply(i);
    }

    private int compute2(int i, Function<Integer, Integer> before, Function<Integer, Integer> after) {
        return before.andThen(after).apply(i);
    }
}

输出结果
4
4
  1. Consumer<T>: 数据消费器,接收一个T类型的对象,无返回值,通常用于设置T对象的值; 单参数无返回值的行为接口;提供了 accept, andThen 方法
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

  1. Predicate<T>: 条件测试器,接收一个T类型的对象,返回布尔值,通常用于传递条件函数; 单参数布尔值的条件性接口。提供了 test (条件测试) ,and(与),negate(非) and-or-negate(与或非),isEqual(等) 方法
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

其中, compose, andThen, and,or,negate用来组合函数接口而得到更强大的函数接口。

Predicate and Consumer test

public class PredicateAndConsumerTest {

    public static void main(String[] args) {
        Student a = new Student("a", 90);
        Student b = new Student("b", 80);
        PredicateAndConsumerTest test = new PredicateAndConsumerTest();
        test.updateStudent(a, student -> student.getScore() >= 90, student -> student.setGrade("A"));
        test.updateStudent(b, student -> student.getScore() <= 90, student -> student.setGrade("B"));
        a.printGrade();
        b.printGrade();
    }

    private void updateStudent(Student student, Predicate<Student> predicate, Consumer<Student> consumer) {
        if (predicate.test(student)) {
            consumer.accept(student);
        }
    }
}

输出
A
B
可以看到Predicate.test()方法进行某些逻辑判断并返回一个boolean值,而Consumer.accept()接受并改变某个对象的内部值

其它的函数接口都是通过这四个扩展而来。

流(Stream)是Java8对函数式编程的重要支撑。大部分函数式工具都围绕Stream展开。

流主要是四类接口:

  • 流到流之间的转换:比如 filter(过滤), map(映射转换), mapTo[Int|Long|Double] (到原子类型流的转换), flatMap(高维结构平铺),flatMapTo[Int|Long|Double], sorted(排序),distinct(不重复值),peek(执行某种操作,流不变,可用于调试),limit(限制到指定元素数量), skip(跳过若干元素) ;
  • 流到终值的转换: 比如toArray(转为数组),reduce(推导结果),collect(聚合结果),min(最小值), max(最大值), count (元素个数), anyMatch (任一匹配), allMatch(所有都匹配),noneMatch(一个都不匹配),findFirst(选择首元素),findAny(任选一元素) ;
  • 直接遍历: forEach (不保序遍历,比如并行流), forEachOrdered(保序遍历) ;
  • 构造流: empty (构造空流),of (单个元素的流及多元素顺序流),iterate (无限长度的有序顺序流),generate (将数据提供器转换成无限非有序的顺序流), concat (流的连接), Builder (用于构造流的Builder对象)

除了 Stream 本身自带的生成Stream 的方法,数组和容器及StreamSupport都有转换为流的方法。比如 Arrays.stream , [List|Set|Collection].[stream|parallelStream] , StreamSupport.[int|long|double|]stream;

流的类型主要有:Reference(对象流), IntStream (int元素流), LongStream (long元素流), Double (double元素流) ,定义在类 StreamShape 中,主要将操作适配于类型系统。

  1. 流的创建方式
public class StreamTest {

    public static void main(String[] args) {
        //流的创建方式
        //1.
        Stream stream1 = Stream.of("hello","world","hello world");
        //2.
        String[] array = new String[]{"hello","world"};
        Stream stream2 = Stream.of(array);
        //3.
        Stream stream3 = Arrays.stream(array);
        //4.通过结合创建,最常见的方式
        List<String> list = Arrays.asList(array);
        Stream stream4 = list.stream();
        
    }
}
  1. 常用方法

forEach方法

List<String> listStr = new ArrayList<>();
listStr.add("hello");
listStr.add("world");
Stream streamStr = listStr.stream();
streamStr.forEach(System.out :: println);
 
输出结果
hello
world

range方法

IntStream.of(new int[]{1,2,3,4,5,6,7,8});
IntStream.range(2,4).forEach(index -> System.out.print(index + " "));
输出结果
2 3 

map reduce方法

List<Integer> listInt = Arrays.asList(1,2,3,4,5);
System.out.print(listInt.stream().map(i -> i * 2).reduce(0,Integer :: sum));
输出结果
30

filter方法

List<Integer> listInt = Arrays.asList(1,2,3,4,5);
listInt.stream().filter(item -> item > 2).forEach(System.out :: println);
输出结果
3
4
5

flatMap方法

将三个数组压平再对每个值求平方

Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2),Arrays.asList(3,4,5),Arrays.asList(7,8,9));
stream.flatMap(theList -> theList.stream()).map(item -> item * item).forEach(System.out :: println);
输出结果
1
4
9
16
25
49
64
81

找出所有单词并且去重

List<String> listDis = Arrays.asList("hello welcome", "world hello", "hello world hello", "hello welcome");

List<String> reList = listDis.stream().map(item -> item.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
reList.forEach(System.out::println);
输出结果
hello
welcome
world

等等还有很多应用的方法,如短路,分组等。。。

聚合器(Collector)

stream里有一个collect(Collectorc)方法,这个方法里面接收一个Collector的实例。它是一个可变的汇聚操作,将输入元素累计到一个可变的结果容器中;它会在所有元素都处理完毕后,将累积的结果转换为一个最终的表示(这是一个可选操作)

example

public class CollectTest {

    public static void main(String[] args) {
        Student a = new Student("a", 95);
        Student b = new Student("b", 92);
        Student c = new Student("c", 92);
        Student d = new Student("d", 89);


        List<Student> studentList = Arrays.asList(a, b, c ,d);

        // 按照分数分组
        Map<Integer, List<Student>> studentMap = studentList.stream().collect(Collectors.groupingBy(Student :: getScore));
        System.out.println("按照分数分组:" + studentMap);

        // 求分组后的平均分
        Map<String, Double> avaMap = studentList.stream().collect(Collectors.groupingBy(Student :: getName, Collectors.averagingDouble(Student :: getScore)));
        System.out.println("按照姓名分组后的平均值:" + avaMap);

        // 分区
        Map<Boolean, List<Student>> parMap = studentList.stream().collect(Collectors.partitioningBy(student -> student.getScore() > 90));
        System.out.println("按照90分分区:" + parMap);

    }
}

输出结果
按照分数分组:{89=[name:d, score:89], 92=[name:b, score:92, name:c, score:92], 95=[name:a, score:95]}
按照姓名分组后的平均值:{a=95.0, b=92.0, c=92.0, d=89.0}
按照90分分区:{false=[name:d, score:89], true=[name:a, score:95, name:b, score:92, name:c, score:92]}

我们也可以自定义Collector

Collector接口

public interface Collector<T, A, R> {
    
    Supplier<A> supplier();

    BiConsumer<A, T> accumulator();

    BinaryOperator<A> combiner();

    Function<A, R> finisher();

    Set<Characteristics> characteristics();
}

其中这里的泛型所表示的含义是:

T:表示流中每个元素的类型。

A:表示中间结果容器的类型。

R:表示最终返回的结果类型。

Collector中还定义了一个枚举类Characteristics,有三个枚举值,

  • Characteristics.CONCURRENT:表示中间结果只有一个,即使在并行流的情况下。所以只有在并行流且收集器不具备CONCURRENT特性时,combiner方法返回的lambda表达式才会执行(中间结果容器只有一个就无需合并)。

  • Characteristics.UNORDER:表示流中的元素无序。

  • Characteristics.IDENTITY_FINISH:表示中间结果容器类型与最终结果类型一致,此时finiser方法不会被调用。

下面实现一个将set转为map的一个Collector

public class MyCollector<T> implements Collector<T, Set<T>, Map<T, T>> {
    @Override
    public Supplier<Set<T>> supplier() {
        return HashSet<T>::new;
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        return Set<T>::add;
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        return (set1, set2)->{
            set1.addAll(set2);
            return set1;
        };
    }

    @Override
    public Function<Set<T>, Map<T, T>> finisher() {
        return (set)->{
            HashMap<T, T> map = new HashMap();
            set.stream().forEach((item)->map.put(item, item));
            return map;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED));
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world", "welcome");
        HashSet<String> set = new HashSet<>();
        set.addAll(list);

        Map<String, String> map = set.stream().collect(new MyCollector<>());
        System.out.println(map);
    }
}

输出结果
{world=world, hello=hello, welcome=welcome}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值