Java8新特性

Java8新特性

Lambda表達(dá)式

概念

lambda表達(dá)式是一段可以傳遞的代碼但指,它的核心思想是將面向?qū)ο笾械膫鬟f數(shù)據(jù)變成傳遞行為
允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)
作用:使用 Lambda 表達(dá)式可以使代碼變的更加簡(jiǎn)潔緊湊。

AndroidStudio 支持Lambda表達(dá)式

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "video.uf.com.java8test"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        jackOptions.enabled = true //支持Java8 Lambda表達(dá)式配置
    }
    //支持Java8 Lambda表達(dá)式 配置
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }


}

語(yǔ)法

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
statment1;
statment2;
//.............
return statmentM;
}

  • 可選類型聲明:不需要聲明參數(shù)類型睬愤,編譯器可以統(tǒng)一識(shí)別參數(shù)值民逼。
  • 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào)蜻展,但多個(gè)參數(shù)需要定義圓括號(hào)喻圃。
  • 可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句屈雄,就不需要使用大括號(hào)。
  • 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值伊磺,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值。
Button btn  = new Button(this);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("sss","ssss");
            }
        });
        btn.setOnClickListener(v -> Log.e("sss","ssss"));

變量作用域

lambda 表達(dá)式只能引用 final 局部變量删咱,這就是說(shuō)不能在 lambda 內(nèi)部修改定義在域外的局部變量屑埋,否則會(huì)編譯錯(cuò)誤。

            int count = 1;
        btn.setOnClickListener(v -> {
            count = 10;//編譯錯(cuò)誤
            Log.e("sss","ssss")});

匿名類和Lambda表達(dá)式的區(qū)別

  • 使用匿名類與 Lambda 表達(dá)式的一大區(qū)別在于關(guān)鍵詞的使用痰滋。對(duì)于匿名類摘能,關(guān)鍵詞 this 解讀為匿名類啼辣,而對(duì)于 Lambda 表達(dá)式怜姿,關(guān)鍵詞 this 解讀為寫就 Lambda 的外部類。
  • Lambda 表達(dá)式與匿名類的另一不同在于兩者的編譯方法摆尝。Java 編譯器編譯 Lambda 表達(dá)式并將他們轉(zhuǎn)化為類里面的私有函數(shù)多艇,它使用 Java 7 中新加的 invokedynamic 指令動(dòng)態(tài)綁定該方法
class TestBean {
        private TestInterface testInterface;
        public TestBean(TestInterface testInterface) {
            this.testInterface = testInterface;
        }
        public void test() {
            if (testInterface != null) {
                testInterface.test();
            }
        }
    }
interface TestInterface {
        void test();
    }
private void tt() {
        TestBean testBean = new TestBean(new TestInterface() {
            @Override
            public void test() {
                System.out.println("AnonymousClass--->" + this.getClass().getName());//video.uf.com.java8test.Test2$1
            }
        });
        testBean.test();
        TestBean testBean1 = new TestBean(() -> System.out.println("Java8--->" + this.getClass().getName()));
        //Java8--->video.uf.com.java8test.Test2
        testBean1.test();
    }

函數(shù)式接口

概念

函數(shù)式接口(Functional Interface)就是一個(gè)具有一個(gè)方法的普通接口逻恐。 @FunctionalInterface注解

特點(diǎn)

可以被隱式轉(zhuǎn)換為lambda表達(dá)式

常用的函數(shù)式接口

java.lang.Runnable

例如:

new Thread(new Runnable() {             
          @Override
          public void run() {
              System.out.println("Create Thread before Java8");
          }
      }).start();   
new Thread(()->System.out.println("Java8-------->")).start(); 

java.util.Comparator

例如:

         List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(5);
        list.add(4);
        System.out.println("origin Data");
        for(Integer item : list){
            System.out.println("---->"+item);
        }

        System.out.println("Sorted Collection before Java8");
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });
        for(Integer item : list){
            System.out.println("---->"+item);
        }
        list = new ArrayList<>();
        list.add(3);
        list.add(5);
        list.add(4);
        System.out.println("Sorted Collection By Java8");
        Collections.sort(list, (item1,item2)->item1-item2);
        for(Integer item : list){
            System.out.println("---->"+item);
        }

java.io.FileFilter

例如:

        File files;
        File[] file;
        try{
            files = new File("/Users/qinzongke/ShareProjects/Java8Studio/app");
            file = files.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory();
                }
            });
            for (File item : file){
                System.out.println("fileFiltertest---->"+item.getAbsolutePath());
            }
            file = files.listFiles(item->item.isFile());
            for (File item : file){
                System.out.println("fileFiltertest---->"+item.getAbsolutePath());
            }

        }catch (Exception e){

        }

Java8新增的函數(shù)接口

java.util.function
主要分為以下幾個(gè)基本類別
Function 函數(shù)型接口 輸入?yún)?shù)為類型T, 輸出為類型R峻黍, 記作 T -> R
Consumer 消費(fèi)型接口 輸入?yún)?shù)為類型T复隆, 輸出為void, 記作 T -> void
Supplier 供給型接口 沒有輸入?yún)?shù)姆涩, 輸出為類型T挽拂, 記作 void -> T
Predicate 斷言型接口 輸入?yún)?shù)為類型T, 輸出為類型boolean骨饿, 記作 T -> boolean

static class AddNewInterface{
      private BiConsumer<Integer,Integer> customer;
      private BiFunction<Integer,Integer,Integer> function;
      
      public AddNewInterface(BiFunction<Integer,Integer,Integer> function){
          this.function =function;
      }

      public AddNewInterface(BiConsumer<Integer,Integer> customer){
          this.customer = customer;
      }

      public void getHandler1(int value1,int value2){
          if(customer != null){
              customer.accept(value1,value2);
          }
      }
      
      public int getHandler2(int value1,int value2){
          if(function != null){
              return function.apply(value1,value2);
          }
          return 0;
      }  
      
private void addNewInterfaceTest(){
        AddNewInterface interface1  = new AddNewInterface(new BiConsumer<Integer,Integer>(){

            @Override
            public void accept(Integer t, Integer u) {
                // TODO Auto-generated method stub
                System.out.println("value1---->"+t);
                System.out.println("value2---->"+u);
            }
            
        });
        AddNewInterface interface2 = new AddNewInterface((value1,value2)->{
            System.out.println("Java8--->value1="+value1);
            System.out.println("Java8---->value2="+value2);
            });
        interface1.getHandler1(10,20);
        interface2.getHandler1(30, 40);
        AddNewInterface interface3 =  new AddNewInterface(new BiFunction<Integer,Integer,Integer>(){

            @Override
            public Integer apply(Integer t, Integer u) {
                // TODO Auto-generated method stub
                return t*u;
            }
        });
        AddNewInterface interface4 = new AddNewInterface((value1,value2)->value1+value2);
        int count1 = interface3.getHandler2(10,20);
        System.out.println("count1---->"+count1);
        int count2 = interface4.getHandler2(10,20);
        System.out.println("count2---->"+count2);
    }

默認(rèn)方法

概念

默認(rèn)方法就是接口可以有實(shí)現(xiàn)方法亏栈,而且不需要實(shí)現(xiàn)類去實(shí)現(xiàn)其方法。
我們只需在方法名前面加個(gè)default關(guān)鍵字即可實(shí)現(xiàn)默認(rèn)方法宏赘。
默認(rèn)方法也可以被重寫

語(yǔ)法

例如:

interface TestInterface{
        int add(int value1,int value2);
        default void test2(String message){
            System.out.println("TestInterface--->test2----->"+message);
        }
        default void test3(String message){
            System.out.println("TestInterface--->test3----->"+message);
        }
    }
private void defaultMethodTest(){
        TestInterface interface1 = (value1,value2)->value1+value2;
        int  count = interface1.add(10,20);
        interface1.test2("aaaaa");
        interface1.test3("bbbbb");
    }

多個(gè)默認(rèn)方法

一個(gè)類實(shí)現(xiàn)了多個(gè)接口绒北,且這些接口有相同的默認(rèn)方法

interface TestInterface{
        int add(int value1,int value2);
        default void test2(String message){
            System.out.println("TestInterface--->test2----->"+message);
        }
        default void test3(String message){
            System.out.println("TestInterface--->test3----->"+message);
        }
    }
interface TestInterface2{
        int add(int value1,int value2);
        default void test2(String message){
            System.out.println("TestInterface2--->test2----->"+message);
        }
        default void test3(String message){
            System.out.println("TestInterface2--->test3----->"+message);
        }

    }
class TestInterfaceClass implements TestInterface,TestInterface2{
        @Override
        public int add(int value1, int value2) {
            return value1+value2;
        }

        @Override
        public void test2(String message) {
            //方法1:創(chuàng)建自己的默認(rèn)方法,來(lái)覆蓋重寫接口的默認(rèn)方法
            System.out.println("TestInterfaceClass---->"+message);
        }

        @Override
        public void test3(String message) {
            //方法2:使用 super 來(lái)調(diào)用指定接口的默認(rèn)方法
            TestInterface2.super.test3(message);
        }
    }

靜態(tài)默認(rèn)方法

interface TestInterface{
        int add(int value1,int value2);
        default void test2(String message){
            System.out.println("TestInterface--->test2----->"+message);
        }
        static void test4(String message){
            System.out.println("TestInterface--->test4--->"+message);
        }
        default void test3(String message){
            System.out.println("TestInterface--->test3----->"+message);
        }
    }
private void defaultStaticMethodTest(){
        TestInterface.test4("defaultStaticMethodTest");
    }

方法引用

作用

我們通常使用lambda表達(dá)式來(lái)創(chuàng)建匿名方法察署。然而镇饮,有時(shí)候我們僅僅是調(diào)用了一個(gè)已存在的方法
方法引用可以使語(yǔ)言的構(gòu)造更緊湊簡(jiǎn)潔,減少冗余代碼。
方法引用使用一對(duì)冒號(hào) ::
例如:

String [] arr = {"a","c","d","b"};
Arrays.sort(arr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareToIgnoreCase(o2);
            }
        });//普通方式
Arrays.sort(arr,(s1,s2)->s1.compareToIgnoreCase(s2));//lambda 方式
Arrays.sort(arr,String::compareToIgnoreCase);//方法引用方式

適用場(chǎng)景: 當(dāng)一個(gè)Lambda表達(dá)式調(diào)用了一個(gè)已存在的方法
不適用場(chǎng)景: 當(dāng)我們需要引用的方法傳其它參數(shù)的時(shí)候储藐,不適合俱济,

public class MethodReferenceBean {
    
    public static void staticMethodTest(){
        System.out.println("i am static method without params");
    }
    public static void staticMethodTest2(MethodReferenceBean bean){
        System.out.println("i am static method without params");
    }
}
private void test1(){
        List<MethodReferenceBean> list = new ArrayList<>();
        for(int i = 0;i<10;i++){
            list.add(new MethodReferenceBean());
        }
        list.forEach(new Consumer<MethodReferenceBean>(){

            @Override
            public void accept(MethodReferenceBean t) {
                // TODO Auto-generated method stub
                MethodReferenceBean.staticMethodTest();
            }
        });
        list.forEach(item->MethodReferenceBean.staticMethodTest());//可以使用
        list.forEach(item->MethodReferenceBean.staticMethodTest2(item));//可以使用
        list.forEach(MethodReferenceBean::staticMethodTest);//不可使用
        list.forEach(MethodReferenceBean::staticMethodTest2);//可以使用;
    }

構(gòu)造器引用

語(yǔ)法:Class::new
一般同Supplier接口聯(lián)合使用 可自定義類似接口 目前Android最低支持的SDKApi為 24
注意:對(duì)目標(biāo)類必須存在無(wú)參的構(gòu)造方法
例如:

public class Test2 {
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub  
        Test2 testBean1 = new Test2();//方法1
        Test2 testBean2 = Test2.newInstance();//方法2
        Test2 testBean3 = Test2.newInstance(Test2::new);//方法3
    }
    
    public static Test2 newInstance(Supplier<Test2> supplier){
        return supplier.get();
    }
    
    public static Test2 newInstance(){
        return new Test2();
    }
}
  • 下一步優(yōu)化
public class InstanceFactory<T> {

    public  static<T> T getInstance(Supplier<T> supplier){
        return supplier.get();
    }
}
public class Test2 {
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub  
        Test2 testBean1 = InstanceFactory.getInstance(Test2::new);
    }
}

針對(duì)構(gòu)造方法傳遞參數(shù)的情況

@FunctionalInterface
public interface InstanceFactorys<T> {
    
    T getInstance(int param1,String param2,String param3);

}
public class Test2 {
    
    private int id;
    private String name;
    private String address;
    
    public Test2(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    
    public Test2() {
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub  
        //InstanceFactorys函數(shù)式接口 接口內(nèi)至存在一個(gè)方法
        InstanceFactorys<Test2> factorys = Test2::new;//必須存在無(wú)參的構(gòu)造方法
        Test2 testBean = factorys.getInstance(1,"name","address");
        System.out.println("id-->"+testBean.id);
        System.out.println("name-->"+testBean.name);
        System.out.println("address-->"+testBean.address);
    }
    
}

Stream API

基本概念

本質(zhì): 對(duì)集合功能的完善,以聲明的形式操作集合钙勃,它就像SQL語(yǔ)句蛛碌,我們只需告訴流需要對(duì)集合進(jìn)行什么操作,它就會(huì)自動(dòng)進(jìn)行操作辖源,并將執(zhí)行結(jié)果交給你蔚携,無(wú)需我們自己手寫代碼。
這種風(fēng)格將要處理的元素集合看作一種流克饶, 流在管道中傳輸酝蜒, 并且可以在管道的節(jié)點(diǎn)上進(jìn)行處理, 比如篩選矾湃, 排序亡脑,聚合等。元素流在管道中經(jīng)過中間操作(intermediate operation)的處理邀跃,最后由最終操作(terminal operation)得到前面處理的結(jié)果霉咨。
例如: 獲取一批學(xué)生年齡>=20并且性別為男的同學(xué)集合并按年齡降序排列打印信息

class Student {
        public String name;
        public int age;
        public int sex;// 1 男 2 女

        public Student(String name, int age, int sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
    }

/**
     * 獲取學(xué)生數(shù)據(jù)源集合
     * @return
     */
    private List<Student> getDataSource() {
        List<Student> dataSource = new ArrayList<>();
        dataSource.add(new Student("test1", 5, 1));
        dataSource.add(new Student("test2", 10, 2));
        dataSource.add(new Student("test3", 15, 2));
        dataSource.add(new Student("test4", 20, 1));
        dataSource.add(new Student("test5", 5, 2));
        dataSource.add(new Student("test6", 10, 1));
        dataSource.add(new Student("test7", 15, 2));
        dataSource.add(new Student("test8", 20, 1));
        dataSource.add(new Student("test9", 25, 2));
        dataSource.add(new Student("test10", 25, 1));
        return dataSource;
    }
/**
     * Java7處理方式
     */
    private void handlerJava7() {
        List<Student> dataSource = getDataSource();
        List<Student> newData = new ArrayList();
        for (Student student : dataSource) {
            if (student.age >= 20 && student.sex == 1) {
                newData.add(student);
            }
        }
        Collections.sort(newData, new Comparator<Student>() {

            @Override
            public int compare(Student s1, Student s2) {
                // TODO Auto-generated method stub
                return s2.age - s1.age;
            }
        });
        for (Student s : newData) {
            System.out.println("name--->"+s.name+" age------>" + s.age);
        }
    }
//Sql處理
select * from student where sex=1 and age >=20 order by age desc
/**
     * Java8處理方式
     */
    private void handlerJava8() {
        List<Student> dataSource = getDataSource();
        dataSource.stream().filter(s -> s.age >= 20 && s.sex == 1).sorted((s1, s2) -> s2.age - s1.age)
                .collect(Collectors.toList()).forEach(s -> System.out.println("name--->"+s.name+" age--->" + s.age));
    }

流的操作步驟

  • 準(zhǔn)備一個(gè)數(shù)據(jù)源
  • 執(zhí)行中間操作

中間操作可以有多個(gè),它們可以串連起來(lái)形成流水線拍屑。

  • 執(zhí)行終端操作

執(zhí)行終端操作后本次流結(jié)束途戒,將獲得一個(gè)執(zhí)行結(jié)果

一個(gè)完整的Stream操作由零個(gè)或多個(gè)中間操作(intermediate operation)和一個(gè)結(jié)束操作(terminal operation)兩部分組成。只有執(zhí)行結(jié)束操作時(shí)僵驰,Stream定義的中間操作才會(huì)依次執(zhí)行喷斋,
從操作的返回值來(lái)看:如果返回值是Stream,那么就是惰性求值蒜茴;如果返回值不是Stream或者是void继准,那么就是及早求值。一個(gè)完整的Stream操作是有多個(gè)惰性求值和一個(gè)及早求值組成

流的獲取

  • 集合 這種數(shù)據(jù)源較為常用矮男,通過stream()方法即可獲取流對(duì)象

對(duì)應(yīng)Collection接口

//獲取串形流
default Stream<E> stream() {
        return StreamSupport.stream(this.spliterator(), false);
    }
//獲取并形流
default Stream<E> parallelStream() {
        return StreamSupport.stream(this.spliterator(), true);
    }

例如

List<Integer> lists = new ArrayList<>();
Stream<Integer> stream =  lists.stream();
  • 數(shù)組 通過Arrays類提供的靜態(tài)函數(shù)stream()獲取數(shù)組的流對(duì)象

例如:

String[] names = {"chaimm","peter","john"};
Stream<String> stream = Arrays.stream(names);  
  • 值 直接將幾個(gè)值變成流對(duì)象
Stream<String> stream =Stream.of("test1","test2","test3");
  • 其他

generator方法移必,返回一個(gè)無(wú)限長(zhǎng)度的Stream,其元素由Supplier接口的提供。在Supplier是一個(gè)函數(shù)接口毡鉴,只封裝了一個(gè)get()方法崔泵,其用來(lái)返回任何泛型的值,該結(jié)果在不同的時(shí)間內(nèi)猪瞬,返回的可能相同也可能不相同憎瘸,沒有特殊的要求。這種情形通常用于隨機(jī)數(shù)陈瘦、常量的 Stream幌甘,默認(rèn)是串行(相對(duì) parallel 而言)但無(wú)序的(相對(duì) ordered 而言)。

 /**
     * Returns an infinite sequential unordered stream where each element is
     * generated by the provided {@code Supplier}.  This is suitable for
     * generating constant streams, streams of random elements, etc.
     *
     * @param <T> the type of stream elements
     * @param s the {@code Supplier} of generated elements
     * @return a new infinite sequential unordered {@code Stream}
     */
    public static<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }
/**
     * generator 生成Stream
     * 配合limit 和filter 使用 否則會(huì)無(wú)限執(zhí)行
     */
    private void generatorSteam(){
        Stream.generate(()->Math.random()).limit(10).forEach(item->System.out.println("--->"+item));;
    }

iterate方法,其返回的也是一個(gè)無(wú)限長(zhǎng)度的Stream锅风,與generate方法不同的是酥诽,其是通過函數(shù)f迭代對(duì)給指定的元素種子而產(chǎn)生無(wú)限連續(xù)有序Stream,其中包含的元素可以認(rèn)為是:seed皱埠,f(seed),f(f(seed))無(wú)限循環(huán)肮帐。

/**
     * Returns an infinite sequential ordered {@code Stream} produced by iterative
     * application of a function {@code f} to an initial element {@code seed},
     * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)},
     * {@code f(f(seed))}, etc.
     *
     * <p>The first element (position {@code 0}) in the {@code Stream} will be
     * the provided {@code seed}.  For {@code n > 0}, the element at position
     * {@code n}, will be the result of applying the function {@code f} to the
     * element at position {@code n - 1}.
     *
     * @param <T> the type of stream elements
     * @param seed the initial element
     * @param f a function to be applied to to the previous element to produce
     *          a new element
     * @return a new sequential {@code Stream}
     */
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @SuppressWarnings("unchecked")
            T t = (T) Streams.NONE;

            @Override
            public boolean hasNext() {
                return true;
            }

            @Override
            public T next() {
                return t = (t == Streams.NONE) ? seed : f.apply(t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iterator,
                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
    }
/**
     * iterate生成Stream
     */
    private void iterateStream(){
        Stream.iterate(2, item->item*item).filter(item->item!=0).forEach(item->System.out.println("---->"+item));
    }

concat方法將兩個(gè)Stream連接在一起,合成一個(gè)Stream边器。若兩個(gè)輸入的Stream都時(shí)排序的训枢,則新Stream也是排序的;若輸入的Stream中任何一個(gè)是并行的忘巧,則新的Stream也是并行的恒界;若關(guān)閉新的Stream時(shí),原兩個(gè)輸入的Stream都將執(zhí)行關(guān)閉處理砚嘴。

 /**
     * Creates a lazily concatenated stream whose elements are all the
     * elements of the first stream followed by all the elements of the
     * second stream.  The resulting stream is ordered if both
     * of the input streams are ordered, and parallel if either of the input
     * streams is parallel.  When the resulting stream is closed, the close
     * handlers for both input streams are invoked.
     *
     * @implNote
     * Use caution when constructing streams from repeated concatenation.
     * Accessing an element of a deeply concatenated stream can result in deep
     * call chains, or even {@code StackOverflowException}.
     *
     * @param <T> The type of stream elements
     * @param a the first stream
     * @param b the second stream
     * @return the concatenation of the two input streams
     */
    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);

        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }
/**
     * concat 組合生成Stream
     */
    private void concatStream(){
        Stream.concat(Stream.of(1,2,3),Stream.of(4,5,6)).forEach(item->System.out.println("---->"+item));
    }

串行流十酣、并行流

串行流:操作由一個(gè)線程串行處理
并行流:把一個(gè)內(nèi)容分成多個(gè)數(shù)據(jù)塊,并用不同的線程分 別處理每個(gè)數(shù)據(jù)塊的流
相互轉(zhuǎn)換:對(duì)流使用parallel()方法將串行流轉(zhuǎn)換為并行流枣宫。對(duì)流使用sequential()方法將并行流轉(zhuǎn)換為串行流。

/**
     * Returns an equivalent stream that is sequential.  May return
     * itself, either because the stream was already sequential, or because
     * the underlying stream state was modified to be sequential.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @return a sequential stream
     */
    S sequential();
/**
     * Returns an equivalent stream that is parallel.  May return
     * itself, either because the stream was already parallel, or because
     * the underlying stream state was modified to be parallel.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @return a parallel stream
     */
    S parallel();

中間操作

中間操作負(fù)責(zé)將一個(gè)流轉(zhuǎn)換為另一個(gè)流吃环,包括 filter()(選擇與條件匹配的元素)也颤、map()(根據(jù)函數(shù)來(lái)轉(zhuǎn)換元素)、distinct()(刪除重復(fù))郁轻、limit()(在特定大小處截?cái)嗔鳎┖?sorted()翅娶。

filter

對(duì)于Stream中包含的元素使用給定的過濾函數(shù)進(jìn)行過濾操作,新生成的Stream只包含符合條件的元素
例如:

private void streamTest(){
        Stream.of(1,2,2,3,3,4,4,5,5,6,6,7,8,9,10).filter(item->item%2 == 0).forEach(item->System.out.println("---->"+item));
    }

distinct

返回去重的Stream

private void streamTest(){
        Stream.of(1,2,2,3,3,4,4,5,5,6,6,7,8,9,10).filter(item->item%2 == 0).distinct().forEach(item->System.out.println("---->"+item));
    }

sorted

返回一個(gè)排序的Stream好唯。默認(rèn)升序排列

private void streamTest(){
//      Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).distinct().sorted().forEach(item->System.out.println("---->"+item));
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).distinct().sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

limit

返回前n個(gè)元素?cái)?shù)據(jù)組成的Stream竭沫。屬于短路操作

private void streamTest(){
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).limit(10).filter(item->item%2 == 0).distinct().sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

skip

返回第n個(gè)元素后面數(shù)據(jù)組成的Stream

private void streamTest(){
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).skip(4).filter(item->item%2 == 0).distinct().sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

map

對(duì)于Stream中包含的元素使用給定的轉(zhuǎn)換函數(shù)進(jìn)行轉(zhuǎn)換操作,新生成的Stream只包含轉(zhuǎn)換生成的元素骑篙。這個(gè)方法有三個(gè)對(duì)于原始類型的變種方法蜕提,分別是:mapToInt,mapToLong和mapToDouble靶端。這三個(gè)方法也比較好理解谎势,比如mapToInt就是把原始Stream轉(zhuǎn)換成一個(gè)新的Stream,這個(gè)新生成的Stream中的元素都是int類型杨名。之所以會(huì)有這樣三個(gè)變種方法脏榆,可以免除自動(dòng)裝箱/拆箱的額外消耗;

private void streamTest(){
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).skip(4).peek(item->System.out.println("peek--->"+item)).filter(item->item%2 == 0).distinct().map(item->item*2).sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

flatMap

和map類似台谍,不同的是其每個(gè)元素轉(zhuǎn)換得到的是Stream對(duì)象须喂,會(huì)把子Stream中的元素壓縮到

private void streamTest(){
        Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5,6)).flatMap(item->item.stream()).map(item->item*2).forEach(item->System.out.println("flat--->"+item));
//      Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).skip(4).peek(item->System.out.println("peek--->"+item)).filter(item->item%2 == 0).distinct().map(item->item*2).sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

peek

生成一個(gè)包含原Stream的所有元素的新Stream,同時(shí)會(huì)提供一個(gè)消費(fèi)函數(shù)(Consumer實(shí)例),新Stream每個(gè)元素被消費(fèi)的時(shí)候都會(huì)執(zhí)行給定的消費(fèi)函數(shù)

private void streamTest(){
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).skip(4).peek(item->System.out.println("peek--->"+item)).filter(item->item%2 == 0).distinct().sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

終端操作

數(shù)據(jù)集的處理在執(zhí)行終止操作時(shí)開始坞生,比如縮減(sum() 或 max())仔役、應(yīng)用 (forEach()) 或搜索 (findFirst()) 操作。終止操作會(huì)生成一個(gè)結(jié)果或無(wú)結(jié)果

forEach

將提供的操作應(yīng)用于流的每個(gè)元素

private void streamTest(){
//      Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5,6)).flatMap(item->item.stream()).map(item->item*2).forEach(item->System.out.println("flat--->"+item));
        Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).skip(4).peek(item->System.out.println("peek--->"+item)).filter(item->item%2 == 0).distinct().map(item->item*2).sorted((item1,item2)->item2-item1).forEach(item->System.out.println("---->"+item));
    }

toArray

使用流的元素創(chuàng)建一個(gè)數(shù)組

Integer[] arrays = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).distinct().toArray(item-> new Integer[item]);

collect

將流的元素聚合到一個(gè)匯總結(jié)果容器中恨胚。

List<Integer> array = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).distinct().collect(Collectors.toList());
Collectors

為Stream的collect提供轉(zhuǎn)換類型

toList

把流中所有元素收集到List中
返回類型: List<T>

toSet

把流中所有元素收集到Set中,刪除重復(fù)項(xiàng)
返回類型:Set<T>

Set<Integer> array = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.toSet());
toCollection

把流中所有元素收集到給定的供應(yīng)源創(chuàng)建的集合中
返回類型:Collection<T>

ArrayList array = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.toCollection(()->new ArrayList<Integer>()));
counting

計(jì)算流中元素個(gè)數(shù)
返回類型:long

long count = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.counting());
summingInt

對(duì)流中元素的一個(gè)整數(shù)屬性求和
返回類型:Integer

int count = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.summingInt(item->item));
averagingInt

計(jì)算流中元素integer屬性的平均值
返回值類型:Double

double count = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.averagingInt(item->item));
joining

連接流中每個(gè)元素的toString方法生成的字符串
返回值類型:String

String str = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).map(item->String.valueOf(item)).collect(Collectors.joining(","));
maxBy

流中按照給定比較器選出的最大元素的optional
如果為空返回的是Optional.empty()
返回值類型:Optional<T>

Optional<Integer> value = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.maxBy((item1,item2)->item1-item2));
minBy

流中按照給定比較器選出的最大元素的optional
如果為空返回的是Optional.empty()
返回值類型:Optional<T>

Optional<Integer> value = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.minBy((item1,item2)->item1-item2));
reducing

從一個(gè)作為累加器的初始值開始,利用binaryOperator與流中的元素逐個(gè)結(jié)合,從而將流歸約為單個(gè)值
返回值類型:T / Optional<T>

int value1 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.reducing(100,(item1,item2)->item1+item2));
int value2 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).collect(Collectors.reducing((item1,item2)->item1+item2)).get();
groupingBy

根據(jù)流中元素的某個(gè)值對(duì)流中的元素進(jìn)行分組,并將屬性值做為結(jié)果map的鍵
返回值類型:Map<K,List<T>>

List<Student> datas = getDataSource();
Map<Integer,List<Student>> data = datas.stream().collect(Collectors.groupingBy((item1)->item1.sex));
partitioningBy

根據(jù)流中每個(gè)元素應(yīng)用謂語(yǔ)的結(jié)果來(lái)對(duì)項(xiàng)目進(jìn)行分區(qū)
返回值類型:Map<Boolean,List<T>>

Map<Boolean,List<Student>> data = datas.stream().collect(Collectors.partitioningBy(item->item.sex==1));

reduce/min/max/count

作用類似Collectors中

int value1 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).limit(3).filter(item->item%2 == 0).reduce((item1,item2)->item1+item2).get();
int value2 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).limit(3).filter(item->item%2 == 0).reduce(100,(item1,item2)->item1+item2);
int value3 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).max((item1,item2)->item1-item2).get();
int value4 = Stream.of(1,2,2,3,3,6,6,5,4,4,5,7,8,9,10).filter(item->item%2 == 0).min((item1,item2)->item1-item2).get();

anyMatch/allMatch/noneMatch

有一個(gè)符合條件返回true/全部符合條件返回true/完全不符合條件返回true

List<Student> datas = getDataSource();
boolean flag1 = datas.stream().anyMatch(item->item.sex==1);
boolean flag2 = datas.stream().anyMatch(item->item.sex==2);
boolean flag3 = datas.stream().anyMatch(item->item.sex==3);
boolean flag4 = datas.stream().noneMatch(item->item.sex == 3);
boolean flag5 = datas.stream().noneMatch(item->item.sex==2);
boolean flag6 = datas.stream().allMatch(item->item.sex == 1);

findFirst/findAny

返回流的第一個(gè)元素(如果有)/返回流的任一個(gè)元素(如果有)

Student student1 = datas.stream().findFirst().get();
Student student2 = datas.stream().findAny().get();

Optional 類

Optional 類是一個(gè)可以為null的容器對(duì)象骂因。如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象赃泡。
Optional 類的引入很好的解決空指針異常

class TestBean {
        public TestBean1 bean1;
        public TestBean(TestBean1 bean1) {
            this.bean1 = bean1;
        }
    }
class TestBean1 {
        public TestBean2 bean2;
        public TestBean1(TestBean2 bean) {
            this.bean2 = bean;
        }
    }
class TestBean2 {
        public String name;
        public TestBean2(String name) {
            this.name = name;
        }
    }
private void testJava7(TestBean bean) {
        if(bean != null){
            if(bean.bean1!=null){
                if(bean.bean1.bean2 != null){
                    System.out.println(bean.bean1.bean2.name);  
                }
            }
        }
    }
private void testJava8(TestBean bean) {
        Optional.ofNullable(bean).map(item->item.bean1).map(item->item.bean2).ifPresent(t->System.out.println(t.name));
    }

創(chuàng)建Optional對(duì)象

  • of

of方法通過工廠方法創(chuàng)建Optional類寒波。需要注意的是,創(chuàng)建對(duì)象時(shí)傳入的參數(shù)不能為null升熊。如果傳入?yún)?shù)為null俄烁,則拋出NullPointerException

TestBean bean = new TestBean(1,"test1",15,1);
Optional<TestBean> test = Optional.of(bean);
TestBean bean1 = null;
Optional<TestBean> test1 = Optional.of(bean1);//拋出異常 java.lang.NullPointerException
  • ofNullable

為指定的值創(chuàng)建一個(gè)Optional,如果指定的值為null级野,則返回一個(gè)空的Optional.ofNullable與of方法相似页屠,唯一的區(qū)別是可以接受參數(shù)為null的情況

TestBean bean = new TestBean(1,"test1",15,1);
Optional<TestBean> test = Optional.ofNullable(bean);
TestBean bean1 = null;
Optional<TestBean> test1 = Optional.ofNullable(bean1);

isPresent

判斷Optional對(duì)象中的值是否存在,若存在返回true,否則返回false

/**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    public boolean isPresent() {
        return value != null;
    }
Optional<String> optional1 = Optional.ofNullable("HelloWordl");
boolean flag = optional1.isPresent();

get

若Optional對(duì)象中的值存在則通過get()獲取,否則拋NoSuchElementException異常

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
Optional<String> optional = Optional.ofNullable("HelloWordl");
String str = optional.get();

ifPresent

如果 Optional 中有值蓖柔,則對(duì)該值調(diào)用 consumer.accept辰企,否則什么也不做

/**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
Optional<String> optional = Optional.ofNullable("HelloWordl");
String str = optional.get();
optional.ifPresent(item->System.out.println(item));

orElse

如果 Optional 中有值則將其返回,否則返回 orElse 方法傳入的參數(shù)

/**
     * Return the value if present, otherwise return {@code other}.
     *
     * @param other the value to be returned if there is no value present, may
     * be null
     * @return the value, if present, otherwise {@code other}
     */
    public T orElse(T other) {
        return value != null ? value : other;
    }
Optional<String> optional = Optional.of("HelloWorld");    
System.out.println(optional.orElse("Java"));
optional = Optional.ofNullable(null);   
System.out.println(optional.orElse("Java"));

orElseGet

orElseGet 與 orElse 方法的區(qū)別在于况鸣,orElseGet 方法傳入的參數(shù)為一個(gè) Supplier 接口的實(shí)現(xiàn) —— 當(dāng) Optional 中有值的時(shí)候牢贸,返回值;當(dāng) Optional 中沒有值的時(shí)候镐捧,返回從該 Supplier 獲得的值潜索。

/**
     * Return the value if present, otherwise invoke {@code other} and return
     * the result of that invocation.
     *
     * @param other a {@code Supplier} whose result is returned if no value
     * is present
     * @return the value if present otherwise the result of {@code other.get()}
     * @throws NullPointerException if value is not present and {@code other} is
     * null
     */
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
Optional<String> optional = Optional.of("HelloWorld");
System.out.println(optional.orElseGet(()->"Java"));
optional = Optional.ofNullable(null);  
System.out.println(optional.orElseGet(()->"Java"));

orElseThrow

orElseThrow 與 orElse 方法的區(qū)別在于,orElseThrow 方法當(dāng) Optional 中有值的時(shí)候懂酱,返回值竹习;沒有值的時(shí)候會(huì)拋出異常,拋出的異常由傳入的 exceptionSupplier 提供

/**
     * Return the contained value, if present, otherwise throw an exception
     * to be created by the provided supplier.
     *
     * @apiNote A method reference to the exception constructor with an empty
     * argument list can be used as the supplier. For example,
     * {@code IllegalStateException::new}
     *
     * @param <X> Type of the exception to be thrown
     * @param exceptionSupplier The supplier which will return the exception to
     * be thrown
     * @return the present value
     * @throws X if there is no value present
     * @throws NullPointerException if no value is present and
     * {@code exceptionSupplier} is null
     */
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
Optional<String> optional = Optional.of("HelloWorld");
System.out.println(optional.orElseThrow(()->new StringNotFoundException("String Not Found")));
optional = Optional.ofNullable(null);
System.out.println(optional.orElseThrow(()->new StringNotFoundException("String Not Found")));

filter

如果有值并且滿足斷言條件返回包含該值的Optional列牺,否則返回空Optional整陌。

Optional<String> optional = Optional.of("HelloWorld");
System.out.println(optional.filter(item->item.length()>10).orElse("NotFoundString"));

map

如果有值,則對(duì)其執(zhí)行調(diào)用mapping函數(shù)得到返回值瞎领。如果返回值不為null蔓榄,則創(chuàng)建包含mapping返回值的Optional作為map方法返回值,否則返回空Optional默刚。

Optional<String> optional = Optional.of("HelloWorld");
System.out.println(optional.filter(item->item.length()>=5).map(item->item+"----->").get());

flatMap

如果有值甥郑,為其執(zhí)行mapping函數(shù)返回Optional類型返回值,否則返回空Optional荤西。flatMap與map(Funtion)方法類似澜搅,區(qū)別在于flatMap中的mapper返回值必須是Optional伍俘。調(diào)用結(jié)束時(shí),flatMap不會(huì)對(duì)結(jié)果用Optional封裝勉躺。

Optional<String> optional = Optional.of("HelloWorld");
System.out.println(optional.filter(item->item.length()>=5).map(item->item+"----->").flatMap(item->Optional.of(item)).get());
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末癌瘾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饵溅,更是在濱河造成了極大的恐慌妨退,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕企,死亡現(xiàn)場(chǎng)離奇詭異咬荷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)轻掩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門幸乒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人唇牧,你說(shuō)我怎么就攤上這事罕扎。” “怎么了丐重?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵腔召,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我扮惦,道長(zhǎng)臀蛛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任径缅,我火速辦了婚禮掺栅,結(jié)果婚禮上烙肺,老公的妹妹穿的比我還像新娘纳猪。我一直安慰自己,他們只是感情好桃笙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布氏堤。 她就那樣靜靜地躺著,像睡著了一般搏明。 火紅的嫁衣襯著肌膚如雪鼠锈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天星著,我揣著相機(jī)與錄音购笆,去河邊找鬼。 笑死虚循,一個(gè)胖子當(dāng)著我的面吹牛同欠,可吹牛的內(nèi)容都是我干的样傍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铺遂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼衫哥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起襟锐,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撤逢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后粮坞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚊荣,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年捞蚂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妇押。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姓迅,死狀恐怖敲霍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丁存,我是刑警寧澤肩杈,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站解寝,受9級(jí)特大地震影響扩然,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聋伦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一夫偶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧觉增,春花似錦兵拢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嘹履,卻和暖如春腻扇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背砾嫉。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工幼苛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焕刮。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓舶沿,卻偏偏與公主長(zhǎng)得像舌剂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暑椰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 原創(chuàng)文章&經(jīng)驗(yàn)總結(jié)&從校招到A廠一路陽(yáng)光一路滄桑 詳情請(qǐng)戳www.codercc.com 對(duì)于Java開發(fā)者來(lái)說(shuō)霍转,...
    你聽___閱讀 2,340評(píng)論 4 38
  • Java 8自Java 5(發(fā)行于2004)以來(lái)最具革命性的版本。Java 8 為Java語(yǔ)言一汽、編譯器避消、類庫(kù)、開發(fā)...
    誰(shuí)在烽煙彼岸閱讀 888評(píng)論 0 4
  • Java 8自Java 5(發(fā)行于2004)以來(lái)最具革命性的版本召夹。Java 8 為Java語(yǔ)言岩喷、編譯器、類庫(kù)监憎、開發(fā)...
    huoyl0410閱讀 627評(píng)論 1 2
  • 第一天 加油???
    饞大喵愛吃魚閱讀 167評(píng)論 1 0
  • 午后的陽(yáng)光纱意,靜靜地灑落,江邊的風(fēng)暖暖的鲸阔,一個(gè)穿著亞麻色高領(lǐng)毛衣偷霉,搭配灰色闊腿褲的年輕女孩,手上拎著一個(gè)小方型的暗紅...
    雪之夢(mèng)_325e閱讀 321評(píng)論 0 2