JDK8新特性-函数式编程

函数式编程-Stream流

1.概述

1.1 原因:公司现在代码大量使用用函数式编程;代码可读性高;简化多层嵌套;处理大数据下集合的效率高(并行流),java并发编程多线程。

1.2 函数式编程思想:面向对象要关注什么对象完成何事,函数编程思想类似于数学函数。关注的是对数据进行什么操作。

1.3 优点

  • 代码简洁,易于理解
  • 容易”并发编程“
  • 内置函数接口,表达高效和方便
  • 汲取了Lisp语言特点
  • 取代了大部分匿名内部类

1.4 对接口要求

  • 函数式接口
  • @FunctionalInterface注解
  • 接口中只有一个被实现的方法

2.Lambda表达式

2.1 概述:是jdk8中的语法糖,对匿名内部类的写法简化。是函数式编程思想的重要提现。不用关注是什么对象,更关注对数据(参数)进行了什么操作(方法体)。

2.2 原则:可推导可简化

2.3 基本格式:(参数列表)->{code}

public class FunctionDemo01 {
    public static void main(String[] args) {
        int i = calcNum((left, right) -> left + right);
        System.out.println(i);
    }

    static int calcNum(IntBinaryOperator operator) {
        int a=10;
        int b=20;
        return operator.applyAsInt(a,b);
    }
}
public static void main(String[] args) {
    /*int i = calcNum((left, right) -> left + right);
    System.out.println(i);*/
    String s = typeConver(new Function() {
        @Override
        public String apply(String s) {

            return s + "zk";
        }
    });
    String s = typeConver(s1 ->s1 + "zk");
    System.out.println(s);

}
static  R typeConver(Function function){
    String str = "456";
    return function.apply(str);
}

2.4 省略规则

  • 参数类型可以省略
  • 方法体只有一句代码时大括号和唯一的代码分号可以省略
  • 方法只有一个参数时小括号也可以省略。

3.Stream流

3.1 概述:jdk8的Stream使用函数式编程模式,被用来对集合和数组进行链状流式操作。

List list = new ArrayList();
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
list = Arrays.asList(arr);
list.stream()   //把集合转换成流
        .distinct() //去重
        .filter(integer -> integer <= 5)   //筛选条件
        .forEach(integer -> System.out.println(integer)); //遍历打印

3.2 常用操作

3.2.1 创建流

单列集合:集合对象.stream()

List list = new ArrayList();
list = Arrays.asList(arr);
list.stream();

数组:Arrays.stream(arr)或Stream.of(arr)创建

Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
Stream stream = Arrays.stream(arr);
Stream stream2 = Stream.of(arr);

双列集合:转换为单列集合后在创建。

Map map = new HashMap<>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
Stream> stream1 = map.entrySet().stream();
stream1.filter(new Predicate>() {
    @Override
    public boolean test(Map.Entry entry) {
        return entry.getValue()>1;
    }
}).forEach(new Consumer>() {
    @Override
    public void accept(Map.Entry entry) {
        System.out.println(entry.getKey()+"==="+entry.getValue());
    }
});
//replace lambda
        stream1.filter(entry -> entry.getValue()>1).forEach(entry -> System.out.println(entry.getKey()+"==="+entry.getValue()));

3.2.2 中间操作:先去重,后比较效率高。

  • filter:过滤集合中的元素,需要条件表达式,全集–>子集
  • map:对流中的元素进行计算和转换
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
Stream stream2 = Stream.of(arr);
stream2.	//参数
        map(integer -> integer + 99)
        .forEach(integer -> System.out.println(integer));
  • distinct:依赖Object的equals方法判断是否相同,注意重写equals方法。
  • sorted:调用空参sort(),比较的如果是流中元素。类则要实现 Comparable接口实现compare方法
public int compareTo(T o){
	return this.x - o.x  //相减 则比较大小,升序降序调换位置
}
List list = new ArrayList();
    Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
    list = Arrays.asList(arr);
    list.stream()   //把集合转换成流
            //.distinct() //去重
            //.filter(integer -> integer <= 5)   //筛选条件
            //.sorted()
            .sorted((o1, o2) -> o2 - o1)   //大小比较,升序降序
            .forEach(integer -> System.out.println(integer));
}
list.stream().sorted(Comparator.comparing(new Function() {
    @Override
    public Integer apply(Integer integer) {
        return integer;
    }
}).reversed()).forEach(num-> System.out.println(num));
//升序可以识别匿名内部类参数类型,参数类型可以省略
list.stream().sorted(Comparator.comparing(integer -> integer)).forEach(num-> System.out.println(num));
//降序无法识别匿名内部类参数类型,方法体因参数类型无法确定而调用失效,参数类型不可以省略
list.stream().sorted(Comparator.comparing((Function) integer -> integer).reversed()).forEach(num-> System.out.println(num));
  • limit:设置流的最大长度,超出部分将被抛弃。超过了没问题。限行操作。内存分页
  • skip:跳过流中前n个元素,返回剩下的元素 :跳行操作,内存分页
//内存分页操作
private static void test05(List list) {
    int page = 0;
    final int pageNum = 3;
    int pageSize = (list.size() % pageNum) == 0? list.size()/pageNum:list.size()/pageNum +1;
    for (int i=0; i < pageSize; i++) {
        page = i*pageNum;
        list.stream().skip(page).limit(pageNum).forEach(x-> System.out.print(x+","));
        System.out.println();
    }
}
  • Map:只能把一个元素转换为另一个对象作为流中的元素。flatMap可以把一个对象转换为多个对象作为流中的元素。
List list = new ArrayList();
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
list = Arrays.asList(arr);
/*list.stream()   //把集合转换成流
        .distinct() //去重
        .filter(integer -> integer <= 5)   //筛选条件
        .forEach(integer -> System.out.println(integer)); //遍历打印*/
//对流中元素里的每个list和数组再转换为Stream对象元素作为流元素,进行操作
list.stream().flatMap(new Function>() {
    @Override
    public Stream apply(Integer integer) {
        List list1 = new ArrayList<>();
        list1.add(String.valueOf(integer)+"qwe");
        return list1.stream();
    }
}).distinct().forEach(s -> System.out.println(s));
list.stream().flatMap(book->Arrays.stream(book.getCategory().split(","))).forEach()
					"xxx,xxx"格式分割为数组,转换为流对象元素
List list = Arrays.asList("ab", "cd", "ef", "gh");
List list2 = Arrays.asList("ij", "kl", "mo", "pq");
//两个一维数组拼接成二维数组
List> collect = Stream.of(list, list2).collect(Collectors.toList());
collect.forEach(list1 -> System.out.println(list1));
//在用flatMap降维度
collect.stream().flatMap(new Function, Stream>() {
    @Override
    public Stream apply(List list) {
        return list.stream();
    }
}).forEach(s -> System.out.println(s));
collect.stream().flatMap((Function, Stream>) list12 -> list12.stream()).forEach(s -> System.out.println(s));

3.2.3 终结操作:用流则必须要有此操作,否则中间操作中的过程不会执行。

  • forEach
stream.distinct() //去重
        .filter(integer -> {
            System.out.println("test");  //不会执行
            ;return integer <= 5;});   //筛选条件
        //.forEach(integer -> System.out.println(integer)); //遍历打印
List list = Arrays.asList("ab", "cd", "ef", "gh");
list.forEach(new Consumer() {
    @Override
    public void accept(String s) {
        System.out.println("匿名内部类方式:"+s);
    }
});
list.forEach(s -> System.out.println("lambda方式:"+s));
  • count:获取当前流中元素的个数(long)。
  • max、min:返回Optional xx; 获取值xx.get();
  • 流经过终结操作后流终止掉,无法在调用流的方法,需要重新获取流。
  • collect:把当前流转换为集合list,set,map
//使用java.util.stream.Collectors;
//工具类里面是静态方法
List collect = stream.collect(Collectors.toList())
//Collectors.toMap两个参数,分别指明key和value值的转换关系
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Map collect1 = list.stream().
        distinct().
        collect(Collectors.toMap(inte -> inte + "abc", inte -> inte));
System.out.println(collect1);
List list1 = Arrays.asList("3", "5", "6", "1", "9", "8");
//使用collcet将流中元素转换为字符串,用特殊符号分割
String str = list1.stream().collect(Collectors.joining(","));
System.out.println(str);
List list = Arrays.asList(3, 5, 2, 1, 3, 5, 2, 1, 3, 5, 2, 1);
//用collect将list转为set去重
list.stream().collect(Collectors.toSet()).forEach(x-> System.out.println(x));

//用collect将list转为LinkedList
list.stream().collect(Collectors.toCollection(new Supplier() {
    @Override
    public LinkedList get() {
        return new LinkedList();
    }
})).forEach(x-> System.out.println(x));
System.out.println("=========");
list.stream().collect(Collectors.toCollection(() -> new LinkedList())).forEach(x-> System.out.println(x));
System.out.println("=========");
list.stream().collect(Collectors.toCollection(LinkedList::new)).forEach(x-> System.out.println(x));
System.out.println("用collect将list转为TreeSet");
list.stream().collect(Collectors.toCollection(TreeSet::new)).forEach(x-> System.out.println(x));
  • 查找与匹配:

    • anyMatch:是否有符合匹配的,返回返回boolean值

    • allMatch:是否都符合匹配条件,返回boolean值

    • noneMatch:都不符合匹配条件,返回boolean值

    • findAny:获取流中任意一元素,不保证获取的一定是流中第一个元素;从刷选结果中组装成Optional,不一定存在得判断

  • List list = new ArrayList();
    Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
    list = Arrays.asList(arr);
    Optional any = list.stream().
            distinct().
            filter(num -> num > 8).findAny();
            //有数据则输出,没有则不会执行方法体,不报空指针异常
    any.ifPresent(num-> System.out.println(num));
    
    • findFirst:
  • reduce归并:对流中的数据按照自定义的计算方式计算出一个结果(缩减操作)。作用把stream的元素组合起来,可以传入一个初始值,按照计算方式依次拿流中元素和初始化值进行计算,计算后在和后面的元素计算

//reduce两个参数的重载形式
T result = identity; //初始值
for(T element:this stream)
result = accumulator.apply(result,element);//自定义方法
return result;
  • map /reduce模式 reduce需要map进行初始化类型。大数据领域,map做数据分发,reduce做统计,产生结果
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Integer results = list.stream().
        distinct().
        reduce(0, (result, ele) -> result + ele);
System.out.println(results);
//求最大值,max底层用了reduce方法
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Integer results = list.stream().
        distinct().  //最小值
        reduce(Integer.MIN_VALUE, (result, ele) -> result < ele?ele:result);
System.out.println(results);
  • concat:拼接转换流、Stream流转换为集合
List list = Arrays.asList(3, 5, 2);
List list1 = Arrays.asList(2, 1, 3);
List list2 = Arrays.asList(1, 3, 5);

//同时拼接3个list
List[] lists = {list,list1,list2};
//设置空stream流中间变量,迭代计算好的结果
Stream streamTmp = Stream.builder().build();
for (List ls: lists) {
    streamTmp = Stream.concat(streamTmp, ls.stream());
}
streamTmp.forEach(x-> System.out.println(x));
//Stream.concat()拼接两个list
Stream result1 = Stream.concat(list.stream(), list1.stream());
//Stream.concat(result1,list2.stream()).forEach(x-> System.out.println(x));

3.3 注意事项

  • 惰性求值(没有终结操作,中间操作不会执行)
  • 流是一次性的,一旦终结操作后,流不能使用
  • 不会影响元数据(正常情况下)

4.Optional

4.1 概述:编写代码时出现空值针异常。要做非空判断。过多的判断会显得臃肿。所以在JDK8中引入Optional,可以避免空值异常

4.2 使用

list.notNull ==>
if (list != null) {

}

4.2.1 创建对象:Optional像包装类,可以把具体的数据封装到Optional对象内部(作为属性)。使用Optional封装好的方法操作封装进去的数据可以避免空指针异常。使用Optional的静态方法ofNullable把数据封装成一个Optional对象。

Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(arr);
//使用,里面是否有值
integers.ifPresent(integers1 -> System.out.println(integers1.length));
  • 实际开发中数据是从数据库中获取的。mybatis3.5可以支持Optional可以把dao方法返回值定义为Optional,mybatis会自动把数据封装为Optional对象返回。将方法返回值设置为Optional对象。

  • 如果确定封装数据不是空可以用Optional的静态方法of来把数据封装为Optional对象。

  • 若方法返回值类型为Optional。而不确定返回值都不为null,这个时候需要把null封装为Optional返回。可以用Optional的静态方法empty来进行封装。

Optional o = integers == null?Optional.empty():Optional.of(arr);
 
  

4.2.2 安全消费值:使用Optional的ifPresent来使用值,会做非空判断,非空时才使用代码

Optional any = list.stream().
        distinct().
        filter(num -> num > 8).findAny();
        //有数据则输出,没有则不会执行方法体,不报空指针异常
any.ifPresent(num-> System.out.println(num));

4.2.3 获取值:获取值自定义处理可以用get方法获取,不推荐有空指针异常。

4.2.4 安全获取值

  • orElseGet:获取数据并设置数据为空时的默认值。数据不空就能获取该数据,为空则根据传入的参数创建对象作为默认值返回
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(arr);
Integer[] integers2 = integers.orElseGet(() -> new Integer[0]);
System.out.println(integers2.length);
=============================
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
        Optional integers = Optional.ofNullable(null);
        Integer[] integers2 =
                integers.orElseGet(() -> {
                    Integer[] integers3 = new Integer[2];
                    integers3[0] = 11;
                    integers3[1] = 22;
                    return integers3;
                });
        System.out.println(integers2.length);
  • orElseThrow:获取数据,数据不空就能获取该数据,为空则根据传入参数创建异常抛出
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(null);
Integer[] integers1 = new Integer[0];
try {
    integers1 = integers.orElseThrow(() -> new RuntimeException("数据为null!"));
} catch (Throwable throwable) {
    throwable.printStackTrace();
}
System.out.println(integers1.length);

4.2.5 过滤:可以用filter方法数据对数据进行过滤。若经过判断从哪有数据变为无数据的Option对象。

 Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
    Optional integers = Optional.ofNullable(arr);
    integers.filter(integers1 -> integers1.length>15).ifPresent(integers12 -> System.out.println(integers12));
    String str = "abc";
    Optional str1 = Optional.of(str);
    str1.filter(strs->strs.matches("abc")).ifPresent(s -> System.out.println(s));
}

4.2.6 判断:Optional.isPresent()判断数据是否存在。提现不出Optional好处,推荐用ifPresent用法;

String str2 = "abc";
Optional str3 = Optional.of(str2).filter(strs -> strs.matches("abc"));
if ( str3.isPresent() ) {//判断过了
    System.out.println(str3.get());/此操作安全
}

4.2.7 数据转换:利用Optional.map方法可以进行数据转换,转换后的数据也是被Optional包装好的,使用上安全。同stream流中的方法一致。

integers.map(integers13 -> integers13.toString() +"abv").ifPresent(st-> System.out.println(st));

5.函数式接口

5.1 概述:只有一个抽象方法的接口称为函数接口。jdk的函数式接口都加上了@FunctionalInterface注解进行标识。是否加上注解,只要接口中只有一个抽象方法,都是函数式接口。

5.2 常见的函数式接口:java.util.function包下

public interface Consumer{void accept(T t); } //按照抽象方法参数列表和返回值类型,可以对传入的参数进行使用

public interface Function{R apply(T t)} //计算转换接口,将参数进行计算或转换并把结果返回

public interface Predicate{boolean test(T t);} //判断接口,对参数进行条件判断,把判断结果返回

public interface Supplier{T get();} //生产接口,在方法中创建对象并返回

5.3 常用的默认方法:单独的stream流不用,自定义函数式方法时会用到。

  • and 使用Predicate接口时需要进行判断条件的拼接,and相等于&&来拼接两个判断条件
  • or 使用Predicate接口时,or相等于||来拼接两个判断条件
  • negate() 使用Predicate接口时,相当于非!true
  • isEqual 两个参数比较的结果
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
        .distinct()   //是Predicate对象的and方法
        .filter(((Predicate) integer -> integer > 3).and(num -> num<8)).forEach(a-> System.out.println(a));
testOptional07(o -> (Integer)o%2==0, o -> (Integer)o>4);
List list = new ArrayList();
int[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
for (int i : arr) {
    if(predicate1.and(predicate2).test(i)){
    	System.out.println("筛选结果:"+i);
	}
}
testOptional08();
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
        .distinct()
        .filter(((Predicate) integer -> integer > 3).negate()).forEach(a-> System.out.println(a));

6.方法引用:使用lambda时

方法体只有一个方法的调用(包括构造方法),可以使用方法引用进一步简化代码

6.1 推荐方法

使用lambda时不需要考虑什么时候用方法应用,用那种方法应用,引用格式是什么、写完lambda方法体只有一行代码。且是方法的调用时用快捷键尝试能否转换为方法引用即可

6.2 基本格式:表达式引用方法

6.2.1 前提

  • 方法引用和实现复用的前提条件:与实现接口参数和返回值类型要与接口中的抽象方法是一致的
  • Lambda引用方法—静态方法的使用
  • Lambda引用方法—非静态(对象)方法的使用

6.2.2 类名或对象名::方法名

    /*接口引用指向实现方法,然后在main方法中使用 类名(静态方法)/对象(成员方法)::方法名*/
    public static void main(String[] args) {
        //静态方法--Lambda表达式引用
        MyFunctionInterface mf = (a, b) -> sumImp(a,b);
        int sum = mf.sum(3, 6);
        System.out.println(sum);
        //静态方法--在其他类中调用本类方法的引用 类::方法名
        MyFunctionInterface mf1 = Demo01::sumImp;
        int sum1 = mf1.sum(9, 8);
        System.out.println(sum1);
        //new对象引用方法对象名::方法名
        Demo01 demo01 = new Demo01();
        MyFunctionInterface mf2 = demo01::subImp;
        int sum2 = mf2.sum(9, 7);
        System.out.println(sum2);

    }
    public static int sumImp(int a,int b){
        return a+b;
    }
    public int subImp(int a,int b){
        return a-b;
    }
}
interface MyFunctionInterface{
    int sum(int a,int b);
}

6.3 语法详解:

6.3.1 引用静态方法:引用类的静态方法:

  • 格式

类名::方法名

  • 前提:重写方法时,方法体只有一行代码,还是调用了某个类的静态方法且要重写抽象方法中所有参数都按照顺序传入这个静态方法中,此时就可以引用类的静态方法。

6.3.2 引用对象的实例方法

对象名::方法名

前提:重写方法时,方法体只有一行代码,还是调用了某个对象的成员方法且要重写抽象方法中所有参数都按照顺序传入这个成员方法中,此时就可以引用对象的成员方法。

List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
StringBuilder sb = new StringBuilder();
list.stream()
        .distinct()
        .map(integer -> integer+"").forEach(str -> sb.append(str));
System.out.println(sb.toString());

list.stream()
                .distinct()    //类静态方法   //对象实例方法
                .map(String::valueOf).forEach(sb::append);

6.3.3 引用类的实例方法

类名::方法名

前提:重写方法时,方法体只有一行代码,还是调用了第一个参数的成员方法且要重写抽象方法中所有参数都按照顺序传入这个成员方法中,此时就可以引用类的实例方法。

public class OptionalDemo02 {
    public static void main(String[] args) {
        String str = subUsing("lambda is easy", new Using() {
            @Override
            public String use(String str, int i, int endIndex) {
                return str.substring(i, endIndex);
            }
        });
        System.out.println(str);
    }
    public static String subUsing(String str,Using using){
        int index = 7;
        int endIndex = 12;
        return using.use(str,index,endIndex);
    }
}
interface Using {
    String use(String str,int i,int length);
}
String str = subUsing("lambda is easy", String::substring);
System.out.println(str);

6.3.4 构造器引用:(接口实例指向类的构造函数)方法体中的一行代码时构造器的话可以使用构造器引用。

  • 声明接口,该接口作为对象的生成器
  • 调用接口方法返回该对象的实例

类名::new

前提:重写方法时,方法体只有一行代码,还是调用了某个类的构造方法且要重写抽象方法中所有参数都按照顺序传入这个构造方法中,此时就可以引用类的实例方法。

private static void testConstructor() {
    List list = new ArrayList();
    Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
    list = Arrays.asList(arr);
    list.stream()
            .distinct()
            .map(String::valueOf)
            .map(StringBuffer::new)
            .forEach(str-> System.out.println(str.append("dnf")));
}
public class Demo02 {
    public static void main(String[] args) {
        AnimalInterface01 ai01 = () -> new Cat();
        ai01.fatchAnimal();
        ai01 = Cat::new;
        ai01.fatchAnimal();
        /*有参构造函数*/
        AnimalInterface02 ai02 = Cat::new;
        Cat cat = ai02.fatchAnimal("来福", 2);
        System.out.println(cat.getName());

    }
}
@FunctionalInterface
interface AnimalInterface01{
    public Cat fatchAnimal();
}
@FunctionalInterface
interface AnimalInterface02{
    public Cat fatchAnimal(String name, int size);
}
class Cat{
    private String name;
    private int size;

    public Cat() {
        System.out.println("无参构造方法!");
    }

    public Cat(String name, int size) {
        System.out.println("含参构造方法!");
        this.name = name;
        this.size = size;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Cat cat = (Cat) o;
        return size == cat.size &&
                Objects.equals(name, cat.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, size);
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", size=" + size +
                '}';
    }
}

7.高级用法

7.1基本数据类型优化:Stream方法使用了泛型,参数和返回值是引用数据类型,在方法体中使用的是基本数据类型,中间经过自动拆装箱的过程,这个过程要消耗时间大数据量时,系统效率受损。

  • Stream提供了针对基本书类型的方法。mapToInt,mapToLong,flatMapToInt
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
        .distinct()
        .mapToInt(value -> value)    //此处做类型转换,后续都用基本数据类型
        .map(operand -> operand+5).forEach(System.out::println);

7.2 并行流:前面的都是串行流

  • 流中有大量元素,使用并行流提高效率。并行流是把认为分配给多个线程去操作,自己实现会复杂,对并发编程有一定功底。用Stream,修改一个方法调用就可以使用并行流来实现。
  • .parallelStream() //直接创建并行流
  • .parallel() //开启多线程
  • .peek() //lambda中调试方法:中间操作
private static void testConstructor3() {
    List list = new ArrayList();
    Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
    list = Arrays.asList(arr);
    list.stream()
            .distinct()
            .parallel()             //开启多线程
            .peek(x-> System.out.println(x+"::"+Thread.currentThread().getName())) //中间操作调试元素
            .reduce((integer, integer2) -> integer+integer2)
    .ifPresent(System.out::println);
}
list.parallelStream()           //直接创建并行流
        .distinct()
        .peek(x-> System.out.println(x+"::"+Thread.currentThread().getName())) //中间操作调试元素
        .reduce((integer, integer2) -> integer+integer2)
        .ifPresent(System.out::println);

你可能感兴趣的:(java知识脉络,java,学习,intellij-idea)