Java-函数式编程-学习纪要

函数式编程

从java8开始支持

详细文章见廖雪峰的wiki

1 - Lambda表达式

函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式。

String[] array = ...
Arrays.sort(array, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

换成函数式:

String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, (s1, s2) -> {
		return s1.compareTo(s2);
});

1.1 - FunctionalInterface

我们把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。

1.2 - 方法引用

方法引用,是指如果某个方法签名和接口恰好一致,就可以直接传入方法引用

public class Main {
  public static void main(String[] args){
    String[] array = ...;
    Array.sort(array,Main::cmp);
  }
  
  static int cmp(String s1,String s2){
    return s1.compareTo(s2);
  }
}

注意:在这里,方法签名只看参数类型和返回类型,不看方法名称,也不看类的继承关系。

1.3 - 构造方法引用

构造方法的引用写法是类名::new

public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("Bob", "Alice", "Tim");
        List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());
        System.out.println(persons);
    }
}

class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }
    public String toString() {
        return "Person:" + this.name;
    }
}

2 - Stream

  • java.io中的流是用来处理文件和网络数据的,而Stream是用来处理任意的java对象实例的

  • Stream API的基本用法:创建一个Stream,然后做若干次转换,最后调用一个求值方法获取真正计算的结果

2.1 - 特点

  1. 它可以“存储”有限个或无限个元素
  2. 一个Stream可以轻易地转换为另一个Stream,而不是修改原Stream本身
  3. 真正的计算通常发生在最后结果的获取,也就是惰性计算
    • 惰性计算的特点:
      1. 一个Stream转换为另一个Stream时,实际上只存储了转换规则,并没有任何计算发生
      2. 也就是说只存了规则,到需要用的时候才会去计算,并产生内存占用.

2.2 - Stream API的特点

  • Stream API提供了一套新的流式处理的抽象序列;
  • Stream API支持函数式编程和链式操作;
  • Stream可以w表示无限序列,并且大多数情况下是惰性求值的
    • 无限序列必须先变成有限序列再打印

2.3 - 创建Stream

  1. Stream.of()

    Stream<String> stream = Stream.of("A", "B", "C", "D");
    
  2. 基于数组

    Stream<String> stream = Arrays.strem(new String[]{"1","2"})
    
  3. 基于Collection

    List<String> list = new Array();
    Stream<String> stream = list.stream();
    
  4. 基于Supplier

    Stream<String> s = Stream.generate(Supplier<String> sp);
    

    基于Supplier创建的Stream会不断调用Supplier.get()方法来不断产生下一个元素,这种Stream保存的不是元素,而是算法,它可以用来表示无限序列。

    public static void main(String[] args) {
    		Stream<Integer> stream = Stream.generate(new NatureNum());
    		stream.limit(100).forEach(System.out::println);
    	}
    
    	static class NatureNum implements Supplier<Integer> {
    		int n = 0;
    
    		public Integer get() {
    			n++;
    			return n;
    		}
    	}
    
  5. 其他方法

    1. 通过一些API提供的接口,直接获得Stream
    2. 正则表达式的Pattern对象有一个splitAsStream()方法,可以直接把一个长字符串分割成Stream序列而不是数组
    Pattern p = Pattern.compile("\\s+");
    Stream<String> s = p.splitAsStream("The quick brown fox jumps over the lazy dog");
    
  6. 基本类型
    因为java的泛型不支持基本数据类型,只能使用Stream<Integer>这样的写法,但这样会有频繁装箱拆箱的性能问题,所以java又提供了IntStream,LongStream,DoubleStream,其目的就是为了提高效率.

2.4 - 使用map

Stream.map()Stream最常用的一个转换方法,它把一个Stream转换为另一个Stream.可以将一种元素类型转换成另一种元素类型.

示例:

Stream<Integer> s = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s.map(n -> n * n);

利用map可以替换常见的数据格式处理代码:去空格,字符串替换,算法处理等.

2.5 - 使用filter

此方法就是用来过滤掉不符合要求的数据

IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
                .filter(n -> n < 5)
                .forEach(System.out::println);

2.6 - 使用reduce

此方法是一个聚合方法,用来将Stream中的数据进行一些操作后合并.

示例:

int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
System.out.println(sum);

其中reduce中的第一个参数为初始值

没有初始值的情况:

Optional<Integer> opt = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce((acc, n) -> acc + n);
if (opt.isPresent()) {
  System.out.println(opt.get());
}

没有初始值的情况时,因为Stream的元素有可能是0个,这样就没法调用reduce()的聚合函数了,因此返回Optional对象,需要进一步判断结果是否存在。

reduce()是聚合方法,聚合方法会立刻对Stream进行计算。

2.7 输出集合

2.7.1 - List
stream.collect(Collectors.toList());
2.7.2 - 数组
String[] array = list.stream().toArray(String[]::new);
2.7.3 - Map
Stream<String> stream = Stream.of("Key1:Value1", "Key2:Value2");
        Map<String, String> map = stream
                .collect(Collectors.toMap(
                        // 把元素s映射为key:
                        s -> s.substring(0, s.indexOf(':')),
                        // 把元素s映射为value:
                        s -> s.substring(s.indexOf(':') + 1)));
2.7.4 - 分组输出
list.stream().collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));

分组输出使用Collectors.groupingBy(),它需要提供两个函数:分组的key和分组的value;

2.8 - 其他操作

2.8.1 - 排序
stream.sorted();
2.8.2 - 去重
stream.distinct();
2.8.3 - 截取
stream.skip(2).limit(3)
2.8.4 - 合并

将2个stream合并为一个

Stream<String> s1 = ...
Stream<String> s2 = ...
// 合并:
Stream<String> s = Stream.concat(s1, s2);
2.8.5 - flatMap

将stream的元素映射出来形成一个新的stream

s.flatMap(list -> list.stream());
2.8.6 - 并行
stream.parallel()

经过parallel()转换后的Stream只要可能,就会对后续操作进行并行处理。我们不需要编写任何多线程代码就可以享受到并行处理带来的执行效率的提升。

2.8.7 - 其他聚合方法
  • count()
  • min(Comparator<? super T> cp)
  • max(Comparator<? super T> cp)
  • IntStreamLongStreamDoubleStream中的sum()average()
  • boolean allMatch()测试是否所有元素均满足测试条件
  • boolean anyMatch()测试是否至少有一个元素满足测试条件
  • forEach()

Stream提供的常用操作有:

转换操作:map()filter()sorted()distinct()

合并操作:concat()flatMap()

并行处理:parallel()

聚合操作:reduce()collect()count()max()min()sum()average()

其他操作:allMatch(), anyMatch(), forEach()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值