Java基礎|函數式編程及Lambda不香嗎?

Java8函數式編程及Lambda

這裏寫圖片描述

1 函數式編程##

###1.1 函數式編程思想###
函數式編程是對行爲的抽象。核心思想是:使用不可變的值和函數,函數對一個值進行處理映射成另一個值。
###1.2 函數式編程警惕###
####(1) 無狀態####
一般所說的狀態可視爲【reference, store, value】這樣的三元組(引用,存儲, 值)。❶ reference也可以叫index或pointer 或 location,store則可看做是一個接受一個reference返回value的函數(具體實現可以是內存單元,或外部文件等);❷ value就是存儲的值了;❸ 狀態變化則是指兩方面了①.reference的改變,②.reference所指的value改變,一般情況下指後者(將名稱綁定機制和賦值機制分開,如Java等語言);❹那函數式編程的無狀態就是指,以上兩者都是不可變的。
####(2) 無副作用####
❶函數式編程強調沒有"副作用",意味着函數要保持獨立,所有功能就是返回一個新的值,沒有其他行爲,尤其是不得修改外部變量的值。❷函數的副作用指的是函數在調用過程中,除了給出了返回值外,還修改了函數外部的狀態,比如,函數在調用過程中,修改了某一個全局狀態。函數式編程認爲,函數的副用作應該被儘量避免。
❸Java函數式編程中,函數式接口對象的實現會引用方法,引用的方法必須返回具體的對象值不允許引用的方法拋異常!這點坑要注意。

2 函數式接口

2.1 函數式接口###

函數式接口是隻包含一個抽象方法的接口。如Runnable,Comparator接口。Java中Lambda無法單獨出現,需要一個函數式接口來盛放。Lambda表達式或方法具體實現就是函數式接口實現。(函數式接口標記註解 @FunctionalInterface)

2.2 Java中最常用的函數式接口###

Function/<T,R>
接受一個輸入參數,返回一個結果。
Consumer<T>
代表了接受一個輸入參數並且無返回的操作。
redicate<T>
接受一個輸入參數,返回一個布爾值結果。
Supplier<T>
無參數,返回一個結果。
UnaryOperator<T>
接受一個參數爲類型T,返回值類型也爲T。
BinaryOperator<T>
代表了一個作用於於兩個同類型操作符的操作,並且返回了操作符同類型的結果。

Java8中所有函數式接口 http://www.runoob.com/java/java8-functional-interfaces.html

##3 Lambda ##
面對大型的數據集合,Java欠缺高效的並行操作,Lambda的就是解決這一問題。Lambda表達式的入參必須是final類型。Lambda表達式本身類型是:函數式接口類型。

3.1 流(Stream)操作集合

Stream是用函數式編程方式在集合類上進行復雜操作的工具。
惰性求值:返回類型是Stream;
及早求值:返回類型是另一個值或空;

  • collect() 由stream值生成列表; map() 值轉換;
  • filter() 條件過濾;
  • flatMap() 多個Stream
  • reduce() 從一組值中生成一個值;
  • limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原 Stream中包含的元素個數小於N,那就獲取其所有的元素;
  • distinct: 對於Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重複的元素;

過濾器:

  • allMatch:是不是Stream中的所有元素都滿足給定的匹配條件
  • anyMatch:Stream中是否存在任何一個元素滿足匹配條件
  • findFirst: 返回Stream中的第一個元素,如果Stream爲空,返回空Optional
  • noneMatch:是不是Stream中的所有元素都不滿足給定的匹配條件

3.2 收集器

收集器作用:從流生成複雜結構的結果。
數據集合轉換–toList()等。
轉換成值–averagingInt()等。
數據分塊–partitioningBy()。
數據分組–groupingBy()。

3.3 數據並行化

並行:多個任務在同一時間段發生,每個任務分別在不同的cpu上並行處理。
併發:兩個或多個任務共享一個時間段在一個cup上處理。

集合的並行流操作,把stream替換成parallelStream就能獲得一個擁有並行能力的流。要避免持有鎖,必要時流框架會自己同步操作。並行流底層沿用fork/join框架實現。

3.4 影響並行流操作的性能因素

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 + "]";
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章