Java8新特性:函数式编程

GitHub

Lambda表达式

Lambda表达式形式

Java 中表达式一共有五中基本形式 , 具体如下:

public class LambdaExpression {
    
    public static void express() {

        // 1----------------------------------------------
        Runnable runnable = () -> System.out.println("Hello World");
        // 2----------------------------------------------
        ActionListener oneArgument = event -> System.out.println("button clicked");
        // 3----------------------------------------------
        Runnable multiStatement = () -> {
            System.out.print("Hello");
            System.out.println(" World");
        };
        // 4----------------------------------------------
        BinaryOperator<Long> add = (x , y) -> x + y;

        new BinaryOperator<Long>() {
            @Override
            public Long apply(Long aLong, Long aLong2) {
                return null;
            }
        };
        // 5----------------------------------------------
        BinaryOperator<Long> addOper = (Long x , Long y) -> x + y;
    }
}
  1. 如1所示Lambda表达式中不含参数,使用空括号()标识没有参数。该Lambda 表达式实现了Runnable接口,该接口也只有一个run方法 , 没有参数,且返回类型为void类型。
  2. 如2所示的lambda表达式包含且只有一个参数,可以省略参数的括号,lambda表达式主题不仅可以是一个表达式,也可以是一段代码块,使用({})大括号将代码块括起来如3所示
  3. 如4所示,这是就有必要思考怎样去阅读该Lambda表达式。这行代码并不是将两数字相加,而是创建了一个函数,用来计算两个数字相加的结果,变量add的类型是BinaryOperator类型。

记住一点很重要,Lambda表达式都可以扩写为原始的匿名类形式,所以当你觉得这个Lambda表达式很负责且不易理解的时候,不妨把它扩写为 匿名类 形式来看

闭包

也许之前遇到过这样的问题,当匿名内部类需要所在方法里的变量的时候,必须把变量声明为 final , 如下所示:

        final int a = 1;
        new Runnable(){
            @Override
            public void run() {
                System.out.println(a);
            }
        };

java8 放松了这一限制,可以不必再把变量声明为 final,但其实该变量实际上仍然为final的。索然无需将变量声明为final , 但在lambda表达式中,也无法用作非终态变量, 如果坚持用作非终态变量(即改变变量的值),编译器就会报错。

函数式接口

上面例子中提到了 ActionListener 接口 , 我们看下它的代码:

public interface ActionListener extends EventListener {

    /**
     * Invoked when an action occurs.
     */
    public void actionPerformed(ActionEvent e);

}

ActionListener 只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。记住,由于 actionPerformed 定义在一个接口里,因此 abstract 关键字不是必需的。该接口也继承自一个不具有任何方法的父接口:EventListener。

我们把这种接口就叫做函数接口。 JDK 8 中提供了一组常用的核心函数接口:

接口参数返回类型描述
Predicate<T>Tboolean用于判别一个对象。比如求一个人是否为男性
Consumer<T>Tvoid用于接收一个对象进行处理但没有返回,比如接收一个人并打印他的名字
Function<T, R>TR转换一个对象为不同类型的对象
Supplier<T>NoneT提供一个对象
UnaryOperator<T>TT接收对象并返回同类型的对象
BinaryOperator<T>(T, T)T接收两个同类型的对象,并返回一个原类型对象

其中 ConsumerSupplier 对应,是一个消费者与一个提供者。 Predicate 用于判断对象是否符合某个条件,经常被用来过滤对象。 Function是将一个对象转换为另一个对象,比如说要装箱或拆箱某个对象。 UnaryOperator 接收和返回同类型对象,一般用于对对象属性进行修改。 BinaryOperator 可以理解为合并对象

集合处理

Stream

Java8 中引入流的概念, 这里的流和我们之前使用的IO流并不一样。 所有继承自Collection的接口都可以转换为Stream 。 假设我们有一个List中存放 Person对象,对象中有 名称 name , 年龄 age 两个字段, 现在求列表中年龄大于20的人数。 不用Stream 我们会这么写:

		int count = 0;
		for (Person person : persons) {
			if(person.getAge() > 20) {
				count ++;
			}
		}

如果用stream的话,则会简单很多:

		long count2 = persons.stream()
				.filter(person -> person.getAge() > 20)
				.count();

实际filter中参数转换为了如下函数进行的调用:

		Predicate predicate = new Predicate<Person>() {
			@Override
			public boolean test(Person o) {
				return o.getAge() > 20 ;
			}
		};
		long count1 = persons.stream()
				.filter(predicate)
				.count();

这是stream的很简单的一个用法,链式调用方法算是一个主流, 这样也更利于理解。

Stream的常用操作

Stream的方法分为两类, 一类叫惰性求值,一类叫及早求值。 判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值 , 如果返回值是Stream 就是惰性求值。 如果调用惰性求值方法,Stream会记录下这个惰性求值方法的过程 , 并没有去计算, 等到及早求值方法后,就会联通一系列惰性求值方法顺序进行计算 , 返回结果。 通用形式为: Stream.惰性求值.惰性求值....惰性求值.及早求值。 整个过程和建造者模式有共通之处, 建造者模式使用一系列的操作配置属性和配置, 最后调用build方法创建对象。

collect(Collectors.toList())

collect(Collectors.toList()) 方法由 Stream 里的值生成一个列表,是一个及早求值操作。可以理解为 Stream 向 Collection 的转换。 注意这边的Collectors.toList() 其实采用了静态导入,看起来十分简洁。

List<String> collect = Stream.of("a", "b", "c").collect(Collectors.toList());

map

如果一个函数可以将一种类型的值转换成另外一种类型, map操作就可以使用该函数, 将一个流中的值转换为一个新的流

List<String> upperCollect = Stream.of("a", "b", "hello").map(item -> item.toUpperCase()).collect(Collectors.toList());

map方法就是接受一个 Function 的匿名函数进行转换:

		Function<String,String> function = new Function<String, String>() {
			@Override
			public String apply(String o) {
				return o.toUpperCase();
			}
		};
		List<String> funcCollect = Stream.of("a", "b", "hello").map(function).collect(Collectors.toList());

filter

遍历集合数据并过滤其中的元素 , 可尝试使用Stream 中提供的新方法 filter

		long count2 = persons.stream()
				.filter(person -> person.getAge() > 20)
				.count();

filter方法就是接收一个Predicate 的匿名函数类,判断对象是否符合条件 , 符合条件才保留下来。

		Predicate predicate = new Predicate<Person>() {
			@Override
			public boolean test(Person o) {
				return o.getAge() > 20 ;
			}
		};
		long count1 = persons.stream()
				.filter(predicate)
				.count();

flatMap

flatMap可用Stream替换 , 然后将多个Stream链接成一个Stream。

		List<Integer> collect = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4))
				.flatMap(item -> item.stream()).collect(Collectors.toList());

flatMap常用的操作就是合并多个 Collection。

max 和 min

Stream 上常用的操作之一是求最大值和最小值, Stream API中的max 和 min操作足以解决这一问题。

		List<Integer> list = Arrays.asList(2,4,6,3,9,4);

		Integer maxInt = list.stream().max(Integer::compareTo).get();
		Integer minInt = list.stream().min(Integer::compareTo).get();

		System.out.println(maxInt);
		System.out.println(minInt);

这里有两点需要注意: 1.max 和 min 方法返回的是一个 Optional 对象 , (类似Google Guava里的 Optional对象一样) 。 Optional 对象封装的就是实际的值,可能为空 , 所以保险起见 , 可以先用 isPresent() 方法判断一下, Optional的引入就是为了解决方法返回 null 的问题。 2.Integer::compareTo 也是属于 Java8 引入的新特性 , 叫作 方法引入 (Method References) ,在这边就是 (int1,int2) -> int1.compareTo(int2) 的简写 。可以自己查阅,不做赘述。

reduce

reduce 操作可以实现从一组值中生成一个值。 在上述例子中用到的 count , min 和 max 方法 , 因为常用而被纳入标准库中 。 事实上 这些方法都是reduce 操作。 如下是一个累加的过程:

long l = Stream.of(1, 2, 3, 4 ).reduce(0, (acc, element) -> acc + element).longValue();

注意reduce的第一个参数 , 这是一个初始值 , 0 + 1 + 2+ 3 + 4 = 10; 实际第二个参数是BinaryOperator<T>的匿名内部类 如果累乘为如下形式

		BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
			@Override
			public Integer apply(Integer integer, Integer integer2) {
				return integer * integer2;
			}
		};
		long l1 = Stream.of(1, 2, 3, 4).reduce(1, operator).longValue();

结果为1 * 1 * 2 * 3 * 4 = 24;

转载于:https://my.oschina.net/LucasZhu/blog/1922919

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值