Java基础|函数式编程及Lambda不香吗?

Java8 函数式编程及 Lambda
这里写图片描述

1 函数式编程

(1)函数式编程思想

函数式编程是对行为的抽象。核心思想是:使用不可变的值和函数,函数对一个值进行处理映射成另一个值。

(2)函数式编程警惕

无状态

一般所说的状态可视为【reference, store, value】这样的三元组(引用,存储, 值)。
❶ reference 也可以叫 index 或 pointer 或 location,store 则可看做是一个接受一个 reference 返回 value 的函数(具体实现可以是内存单元,或外部文件等);
❷ value 就是存储的值了;
❸ 状态变化则是指两方面了①.reference 的改变,②.reference 所指的value 改变,一般情况下指后者(将名称绑定机制和赋值机制分开,如 Java等语言);
❹那函数式编程的无状态就是指,以上两者都是不可变的。

无副作用

❶函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
❷函数的副作用指的是函数在调用过程中,除了给出了返回值外,还修改了函数外部的状态,比如,函数在调用过程中,修改了某一个全局状态。函数式编程认为,函数的副用作应该被尽量避免。
❸Java 函数式编程中,函数式接口对象的实现会引用方法,引用的方法必须返回具体的对象值不允许引用的方法抛异常!这点坑要注意。


2 函数式接口

(1)函数式接口

函数式接口是只包含一个抽象方法的接口。如 Runnable、Comparator 接口。Java 中 Lambda 无法单独出现,需要一个函数式接口来盛放。Lambda 表达式或方法具体实现就是函数式接口实现。(函数式接口标记注解 @FunctionalInterface)

(2)Java 中最常用的函数式接口

//Java8 中所有函数式接口在包 java.util.function 下。
//可参考(http://www.runoob.com/java/java8-functional-interfaces.html)

Function<T,R>
接受一个输入参数,返回一个结果。
Consumer<T>
代表了接受一个输入参数并且无返回的操作。
Predicate<T>
接受一个输入参数,返回一个布尔值结果。
Supplier<T>
无参数,返回一个结果。
UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。
BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果。

3 Lambda

面对大型的数据集合,Java 欠缺高效的并行操作,Lambda 的就是解决这一问题。Lambda 表达式的入参必须是 final 类型。Lambda 表达式本身类型是:函数式接口类型。

(1)流(Stream)操作集合

Stream是用函数式编程方式在集合类上进行复杂操作的工具。
惰性求值:返回类型是Stream;
及早求值:返回类型是另一个值或空;

  • collect() 由stream值生成列表; map() 值转换;
  • filter() 条件过滤;
  • flatMap() 多个Stream
  • reduce() 从一组值中生成一个值;
  • limit: 对一个Stream进行截断操作,获取其前N个元素,如果原 Stream中包含的元素个数小于N,那就获取其所有的元素;
  • distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;

(2)过滤器

  • allMatch:是不是Stream中的所有元素都满足给定的匹配条件
  • anyMatch:Stream中是否存在任何一个元素满足匹配条件
  • findFirst: 返回Stream中的第一个元素,如果Stream为空,返回空Optional
  • noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件

(3)收集器

收集器作用:从流生成复杂结构的结果。
数据集合转换–toList()等。
转换成值–averagingInt()等。
数据分块–partitioningBy()。
数据分组–groupingBy()。

(4)数据并行化

并行:多个任务在同一时间段发生,每个任务分别在不同的cpu上并行处理。
并发:两个或多个任务共享一个时间段在一个cup上处理。

集合的并行流操作,把stream替换成parallelStream就能获得一个拥有并行能力的流。要避免持有锁,必要时流框架会自己同步操作。并行流底层沿用fork/join框架实现。

(5)影响并行流操作的性能因素

1 数据规模大小,数据够大并行才有意义
2 数据源结构 对于集合,ArrayList(优);HashSet,TreeSet(一般);LinkedList(差)
3 装箱 基本类型处理要更快
4 核的数量 多核cpu才有意义
5 无状态操作性能更优 如map,filter无状态操作,sorted 有状态操作。

4 Lambda单元测试

1 lambda 表达式中建议使用方法引用。
2 使用 peek 方法,加入断点调试。

5 Optional方法

.of
	为非null的值创建一个Optional。为null,则抛出NullPointerException。
	
.ofNullable
	为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。
	
.isPresent
	如果值存在返回true,否则返回false。
	
.get
	如果Optional有值则将其返回,否则抛出NoSuchElementException。
	
.ifPresent
	如果Optional实例有值则为其调用consumer,否则不做处理。

.orElse
	如果有值则将其返回,否则返回指定的其它值。

.orElseGet
	orElseGet与orElse方法类似,区别在于得到的默认值。
	orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。

.orElseThrow
	如果有值则将其返回,否则抛出supplier接口创建的异常。
	在orElseGet方法中,我们传入一个Supplier接口。
	然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。

6 代码示例

Lamba使用示例

/**
 * @author bilahepan
 * @version $Id: LambdaTest.java, v 0.1 2017年6月5日 下午3:32:55 bilahepan Exp $
 */
public class LambdaTest {
    @Test
    public void testUseThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使用匿名内部类开启线程。");
            }
        }).start();
        new Thread(() -> System.out.println("使用lambda开启线程。")).start();

        //直接使用Runnable
        Runnable thread1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("使用匿名内部类开启线程。");
            }
        };
        Runnable thread2 = () -> System.out.println("使用lambda开启线程。");
        thread1.run();
        thread2.run();
    }

    @Test
    public void testCommonMethod() {
        List<List<String>> list = DataFactory.initData();
        //lambda基本用法
        list.parallelStream()
        .flatMap(item -> item.stream())
        .filter(item -> isStart(item))//redicate<T>
        .peek(item->System.out.println(item.toString()))
        .map(item -> caseConvert(item))//Function<T,R>
        .collect(Collectors.toList())
        .forEach(item -> System.out.println(item));
    }

    @Test
    public void testReduce()
    {
        //规约操作-从一组值中生成一个值。count,min,max
        //BinaryOperator<T>  代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果。
        System.out.println(DataFactory.initList().stream().count());
        //这几个操作返回的都是一个Optional对象
        System.out.println(DataFactory.initList().stream().min(Comparator.comparing(item->item.getPrice())).get());
        System.out.println(DataFactory.initList().stream().max(Comparator.comparing(item->item.getPrice())).get());
        System.out.println(DataFactory.initList().stream().map(item->item.getPrice()).reduce(add()).get());
    }
    
    
    @SuppressWarnings("unused")
    @Test
    public void testCollector() {
        //收集器
        //使用内置的收集器--数据集合转换
        DataFactory.initList() .stream()
                               .map(item -> item.getName())
                               .collect(Collectors.toList())
                               .forEach(item -> System.out.println(item));
        //使用内置的收集器--转换成值
        double average = DataFactory.initList()
                                    .stream()
                                    .collect(Collectors.averagingInt(item -> item.getPrice()));

        //数据分块--partitioningBy
        Map<Boolean, List<Book>> partMap = DataFactory.initList()
                                                       .stream()
                                                       .collect(Collectors.partitioningBy(item -> item.getPrice() > 40));
        List<Book> bookList1 = partMap.get(true);
        List<Book> bookList2 = partMap.get(false);
        
        //数据分组--groupingBy
        Map<String, List<Book>> groupMap = DataFactory.initList().stream().collect(Collectors.groupingBy(item->item.getName()));
        groupMap.entrySet().iterator();
    }
    //--------------------------------------------------------私有成员方法--------------------------------------------------------
    /**
     * 
     * @return BinaryOperator<Integer>
     */
    private BinaryOperator<Integer> add() {
        return (a, b) -> a + b;
    }

    /**
     * 
     * @param str
     * @return
     */
    private String caseConvert(String str) {
        if (str.startsWith("a", 0)) {
            return str.toUpperCase();
        } else {
            return str.toLowerCase();
        }
    }

    /**
     * 
     * @param str
     * @return
     */
    private boolean isStart(String str) {
        return (str.startsWith("a", 0) || str.startsWith("b", 0));
    }
}

DataFactory初始化数据使用类

/**
 * @author bilahepan
 * @version $Id: DataFactory.java, v 0.1 2017年11月3日 下午12:35:08 bilahepan Exp $
 */
public class DataFactory {
    /**
     * @return
     */
    public static LinkedList<Book> initList() {
        LinkedList<Book> list = new LinkedList<>();
        list.add(new Book("a", 10));
        list.add(new Book("a", 20));
        list.add(new Book("b", 20));
        list.add(new Book("b", 30));
        list.add(new Book("c", 30));
        list.add(new Book("c", 40));
        list.add(new Book("d", 40));
        list.add(new Book("d", 50));
        return list;
    }

    /**
     * @return
     */
    public static List<List<String>> initData() {
        List<List<String>> list = new LinkedList<>();

        LinkedList<String> item1 = new LinkedList<>();
        item1.add("anbnb");
        item1.add("ahhhk");
        item1.add("a1212");

        LinkedList<String> item2 = new LinkedList<>();
        item2.add("bnbnb");
        item2.add("bhhhk");
        item2.add("b1212");

        LinkedList<String> item3 = new LinkedList<>();
        item3.add("cnbnb");
        item3.add("chhhk");
        item3.add("c1212");

        LinkedList<String> item4 = new LinkedList<>();
        item4.add("dnbnb");
        item4.add("dhhhk");
        item4.add("d1212");

        list.add(item1);
        list.add(item2);
        list.add(item3);
        list.add(item4);
        return list;
    }
}

Book 类

/**
 * @author bilahepan
 * @version $Id: Book.java, v 0.1 2017年11月3日 下午12:24:40 bilahepan Exp $
 */
public class Book {
    /** name */
    private String            name;

    /** price */
    private int            price;

    /**
     * 
     */
    public Book() {
    }

    /**
     * @param name
     * @param price
     */
    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    /**
     * Getter method for property <tt>name</tt>.
     * 
     * @return property value of name
     */
    public String getName() {
        return name;
    }

    /**
     * Setter method for property <tt>name</tt>.
     * 
     * @param name value to be assigned to property name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Getter method for property <tt>price</tt>.
     * 
     * @return property value of price
     */
    public int getPrice() {
        return price;
    }

    /**
     * Setter method for property <tt>price</tt>.
     * 
     * @param price value to be assigned to property price
     */
    public void setPrice(int price) {
        this.price = price;
    }

    /** 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Book [name=" + name + ", price=" + price + "]";
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不甩锅的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值