ElasticSearch、java的四大内置函数式接口、Stream流、parallelStream背后的技术、Optional类

第四周笔记

一、ElasticSearch

1.安装

apt-get install lrzsz

adduser -m es

创建用户组:

useradd *-m* xiaoming (用户名) *PS:追加参数-m*
passwd xiaoming(用户名)

passwd xiaoming

输入新的 UNIX 密码:

重新输入新的 UNIX 密码:

passwd:已成功更新密码

最大文件描述符数(max file descriptors)太低,需要增加至少到65535。

ulimit -Hn 65536

ulimit -Hn

最大虚拟内存区域数(max virtual memory areas)过低,需要增加至少到262144。

  1. 使用root权限登录到Elasticsearch所在的服务器。

  2. 打开sysctl.conf文件,可以使用vim或者nano等文本编辑器打开:

    复制代码sudo vim /etc/sysctl.conf
    
  3. 在文件末尾添加以下内容:

    复制代码vm.max_map_count=262144
    
  4. 保存并关闭文件。

  5. 执行以下命令使配置生效:

    复制代码sudo sysctl -p
    
  6. 重新启动Elasticsearch:

    复制代码/path/to/elasticsearch/bin/elasticsearch
    

curl -O 路径下载文件

vi /etc/sysconfig/network-scripts/ifcfg-ens33

sudo yum install lrzsz

压缩:

undefined 复制代码
tar -zcvf 文件名 压缩路径 

解压:

undefined 复制代码
tar -zxvf 文件名

sudo mkdir -p /root/es/elasticsearch-7.9.3/log

sudo yum install vim

sudo chown -R qingyue:qingyue /usr/local/es/elasticsearch-7.9.3/

[超详细的ElasticSearch安装使用教程视频_哔哩哔哩_bilibili

新增PUT

PUT新增索引,已存在会报错(幂等)

查询GET

查询全部索引

POST

文档的创建

不指定id

指定id

删除

二、java的四大内置函数式接口

1、Supplier(生产型接口)

Supplier:这是一个不接受任何参数并产生一个结果的函数。泛型类型 T 是返回结果的类型。例如,你可以使用 Supplier 接口来生成一个随机数:

1.1、get()方法
1.1.1示例:
java 复制代码
package supplierTest;

import java.util.function.Supplier;

/*
    常用的函数式接口
    java.util.fuction.Supplier<T>接口仅包含一个无参的方法:T get();用来获取一个泛型参数指定类型的对象数据

    Supplier<T>接口被称之为生产型接口,泛型执行String,get方法就会返回一个String
 */
public class SupplierTest {
    // 定义一个方法,方法的参数传毒Supplier<T>接口,泛型执行String,get方法就会返回一个String;
    public static String getString(Supplier<String> supplier) {
        return supplier.get();
    }
    
    public static void main(String[] args) {
        // 调用getString方法,方法的参数Supplier是一个函数式接口,所以可以传递Lambda表达式;
        String str1 = getString(() -> {
            return "hello";
        });
        System.out.println(str1);

        // 优化Lambda表达式(只有一条语句,可以省略{},又是return语句,可以省略return)
        String str = getString(() -> "Hello");
        System.out.println(str);
    }
}
1.1.2练习
java 复制代码
package supplierTest;

import java.util.function.Supplier;

public class SupplierTest2 {

    public static Integer getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }

    public static void main(String[] args) {
        int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int max = getMax(() -> {
            int maxNum = arr[1];
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] > maxNum) {
                    maxNum = arr[i];
                }
            }
            return maxNum;
        });
        System.out.println(max);
    }
}

2、Consumer(消费型接口)

这是一个接受一个参数并返回 void 的函数。泛型类型 T 是输入参数的类型。例如,你可以使用 Consumer 接口来打印一个字符串:

1.1、appect()方法
java 复制代码
package ConsumerTest;

import java.util.function.Consumer;

/*
    java.util.functional.Consumer<T> 接口则正好与Supplier接口相反,
    它是不是生产一个数据,而是消费一个数据,其数据类型有泛型决定
    Consumer接口中包含抽象发放void accept(T t),意为消费一个指定泛型的数据。

    Consumer接口是一个消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据,治愈具体则呢么消费(使用),需要自定义(输出,计算......)
 */
public class ConsumerTest1 {
    /*
     *       定义一个方法
     *       方法的参数传递一个字符串的姓名
     *       方法的参数传递Consumer接口,泛型使用String
     *       可以使用Consumer接口消费字符串的姓名
     *
     * */


    public static void method(String name, Consumer<String> consumer) {
        consumer.accept(name);
    }

    public static void main(String[] args) {
        method("hahah", (String name) -> {
            System.out.println(name);
        });

        method("hahah", (String name) -> {
            // 调用StringBuffer的reverse方法,将字符串反转
            String reName = new StringBuffer(name).reverse().toString();
            System.out.println(reName);
        });
    }

}
1.2 andThen方法示例
java 复制代码
package ConsumerTest;

import java.util.function.Consumer;

/*
 *   Consumer接口的默认方法andThen()
 *   作用:需要两个Consumer接口,可以把连个Consumer接口组合到一起,再对数据解析消费
 *   例如:
 *       Consumer<String> consumer1;
 *       Consumer<String> consumer2;
 *       String s = "hello world";
 *       consumer1.accpet(s);
 *       consumer2.accpet(s);
 *
 *       consumer.andThen(consumer2).accept(s);谁在前谁先消费
 * */
public class AndThenTest {

    // 定义一个方法,方法的参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用字符串
    public static void method(String s, Consumer<String> con1, Consumer<String> con2) {
//        con1.accept(s);
//        con2.accept(s);
        // 使用adnThen()方法,把两个Consumer接口组合到一起,再对数据解析消费
        con1.andThen(con2).accept(s);
    }

    public static void main(String[] args) {
        // 调用method方法,传递一个字符串,两个Lamnbda表达式
        method("hello",
                (t) -> {
                    System.out.println(t.toUpperCase());
                },
                (t) -> {
                    System.out.println(t.toLowerCase());
                });
    }

}
1.3 andThen方法练习
java 复制代码
package ConsumerTest;

import java.util.function.Consumer;

public class AndThenTest2 {
    // 定义一个方法,参数传递String类型的数组和两个Consumer接口,泛型用String
    public static void printInfo(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2) {
        for (String s : arr) {
            consumer1.andThen(consumer2).accept(s);
        }
    }

    public static void main(String[] args) {
        String[] arr = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
        printInfo(arr, (message) -> {
            String name = message.split(",")[0];
            System.out.println("姓名:"+ name);
        }, (message) -> {
            String sex = message.split(",")[1];
            System.out.println("性别:"+sex);
        });
    }
}

3、Predicate(断言型接口)

这是一个接受一个参数并返回一个布尔值的函数。泛型类型 T 是输入参数的类型。例如,你可以使用 Predicate 接口来检查一个字符串是否为空:

1.1、test() 方法
java 复制代码
package PreicateTest;

import java.util.function.Predicate;

/*
 *      java.util.fuction.Predicate<T>接口 判断性接口
 *      作用:对某种数据类型的数据进行判断,结果返回一个Boolean类型
 *
 *       Predicate接口中包含一个抽象发放:
 *           boolean test(T t):用来指定数据类型进行判断的方法
 *               结果:
 *                   符合条件返回true,否则返回false
 *
 * */
public class PredicateTest {

    /*
     *       定义一个方法
     *       参数传一个String类型的字符串
     *       传递与一个Predicate接口,泛型使用String
     *       使用Predicate中的方法test对字符串进行判断,并返回判断的结果返回
     * */

    public static Boolean chekString(String s, Predicate<String> predicate) {
        return predicate.test(s);
    }


    public static void main(String[] args) {
        String s = "hello";
        Boolean a = chekString(s, (message) -> message.length() > 5
        );
        System.out.println(a);
    }
}
1.2、and() 方法
java 复制代码
package PreicateTest;

import java.util.function.Predicate;

/*
 *       逻辑表达式:可以连接多个判断的条件
 *       && : 与
 *       || : 或
 *       ! : 非(取反)
 *
 *       Predicate方法中的默认方法and,表示并且关系,可以用于连接两个判断条件
 *
 * */
public class PredicateAndTest {

    /*     需求:判断一个字符串,有两个判断条件
     *           1.判断字符串的长度是否大于5
     *           2.判断字符串中是否包含a
     *       两个条件必须同时满足
     * */

    public static Boolean chekPredicateString(String s, Predicate<String> predicate, Predicate<String> predicate2) {
//        return predicate.test(s) && predicate2.test(s);
        return predicate.and(predicate2).test(s);
    }


    public static void main(String[] args) {
        String s = "hello";
        boolean a = chekPredicateString(s, (message) -> message.length() > 5,
                (message) -> message.contains("a"));
        System.out.println(a);
    }

}
1.3 or() 方法
java 复制代码
package PreicateTest;

import java.util.function.Predicate;

/*     需求:判断一个字符串,有两个判断条件
 *           1.判断字符串的长度是否大于5
 *           2.判断字符串中是否包含a
 *       两个条件有一个满足
 *
 *          Predicate方法中的默认方法or,表示或关系,可以用于连接两个判断条件
 * */
public class PredicateOrTest {


    public static Boolean chekStringOr(String s, Predicate<String> predicate, Predicate<String> predicate2) {
//        return predicate.test(s) || predicate2.test(s);
        return predicate.or(predicate2).test(s);
    }

    public static void main(String[] args) {
        String s = "hello";
        boolean a = chekStringOr(s, (message) -> message.length() > 5
                , (message) -> message.contains("o"));
        System.out.println(a);
    }
}
1.4 negate() 方法
java 复制代码
package PreicateTest;

import java.util.function.Predicate;

public class PredicateNegateTest {

    /*     需求:判断一个字符串的长度是否大于5
                如果字符串的长度大于5,那么返回false,反之

            Predicate方法中的默认方法negate,表示取反关系,
     * */


    public static Boolean chekStingNegate(String s, Predicate<String> predicate, Predicate<String> predicate2) {
//        return !(predicate.and(predicate2).test(s));
        return predicate.and(predicate2).negate().test(s);
    }

    public static void main(String[] args) {
        String s = "hello";
        boolean a = chekStingNegate(s, (message) -> message.length() > 5,
                (message) -> message.contains("e"));
        System.out.println(a);
    }
}

3、Function<T, R>(函数型接口)

这是一个接受一个参数并产生一个结果的函数。泛型类型 T 是输入参数的类型,而 R 是返回结果的类型。例如,你可以使用 Function 接口将一个字符串转换为大写:

1.1、apply() 方法
java 复制代码
package FunctionTest;


import java.util.function.Function;

/*
 *        java.util.fuction.Function<T,R>接口 用来根据一个类型的数据得到另一个类型的数据,
 *               前者称为前置条件,后者称为后置条件
 *        Function接口中最主要的抽象方法为:R apply(T t) 根据类型T的参数获取类型R的结果
 *               使用场景如:将String类型转换为Integer类型
 * */
public class FunctionTest {

    /*
     *       定义一个方法
     *       方法的参数传递一个字符串类型的整数
     *       方法的参数传递一个Function接口,泛型使用<String, Integer>
     *       使用Function接口中的方法apply,把字符串类型的整数,转换为Integer类型的整数
     * */
    public static Integer change(String s, Function<String, Integer> fun) {
//        Integer i = fun.apply(s);
        int i = fun.apply(s); // 自动拆箱
        System.out.println(i);
        return i;
    }

    public static void main(String[] args) {
        String s = "123456";
//        change(s, (message) -> Integer.parseInt(message));
        change(s, Integer::parseInt);
    }

}
1.2、andThen() 方法
java 复制代码
package FunctionTest;


import java.util.function.Function;

/*
 *       Function接口中的默认方法andThen:用来进行组合操作
 * */
public class FunctionAndThenTest {
    /*       需求:
     *          把String类型的"123",转换为Integer类型,把转换后的结果加10
     *          把增加之后的Integer类型的数据,转换为String类型
     *       分析:
     *          转换了两次
     * */
    public static void chek(String s, Function<String, Integer> function, Function<Integer, String> function2) {
        String a = function.andThen(function2).apply(s);
        System.out.println(a);
    }

    public static void main(String[] args) {
        String s = "123456";
        chek(s, (message) -> Integer.parseInt(message) + 10
                , (message) ->Integer.toString(message) // 只能是int或者Integer类型
//            return String.valueOf(message); // 所有类型都可以
        );
    }
}
1.3 andThen()方法练习
java 复制代码
package FunctionTest;

import java.util.function.Consumer;
import java.util.function.Function;

public class FunctionTestDemo {

    public static Integer change(String s, Function<String, String> function1, Function<String, Integer> function2, Function<Integer, Integer> function3) {
        return function1.andThen(function2).andThen(function3).apply(s);

    }

    public static void main(String[] args) {
        String s = "清月,20";
        Integer a = change(s, (message) -> message.split(",")[1],
                (String1) -> Integer.parseInt(String1),
                (Integer1) -> Integer1 + 100);
        System.out.println(a);
    }
}

三、Stream流

1、Stream的两种获取方式

(1)通过Collection接口中的默认方法Stream stream()
(2)通过Stream接口中的静态of方法
java 复制代码
package StreamAPI;

import java.util.*;
import java.util.stream.Stream;

public class StreamTest02 {

    public static void main(String[] args) {
        // 方法一: 根据Collection获取流
        // Collection接口中有一个默认的方法:default Stream<E> stream
        ArrayList<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        HashSet<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        HashMap<String, String> map = new HashMap<>();
        Stream<String> stream3 = map.keySet().stream();
        Stream<String> stream4 = map.values().stream();
        Stream<Map.Entry<String, String>> stream5 = map.entrySet().stream();

        // 方式二:Stream中的静态方法of获取流
        // static<t> Stream<T> of(T... values)
        Stream<String> stream6 = Stream.of("aa", "bb", "cc");
        String[] strings = {"aa", "bb", "cc"};
        Stream<String> stream7 = Stream.of(strings);

        // 基本数据类型的数组不行,会将整个数组看做一个元素进行操作
        int[] arr = {11, 22, 33};
        Stream<int[]> stream8 = Stream.of(arr);
    }
}

2、Stream的注意事项和常用方法

1、注意事项

1.Stream只能操作一次

2.Stream方法返回的是新的流

3.Stream不调用终结方法,中间的操作不会执行

java 复制代码
package StreamAPI;

import java.util.stream.Stream;

public class StreamTest03 {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("aa", "bb", "cc");

        // 1.Stream只能操作一次
//        long count = stream1.count();
//        long count2 = stream1.count();

        // 2.Stream方法返回的是新的流
//        Stream<String> stream2 = stream1.limit(1);
//        System.out.println(stream1);
//        System.out.println(stream2);

        // 3.Stream不调用终结方法,中间的操作不会执行
        stream1.filter((s) -> {
            System.out.println(s);
            return true;
        }).count();
    }
}
2、常用方法

(1)终结方法:返回值类型不再是Stream类型的方法,不再支持链式调用。
(2)非终结方法:反之

方法名 方法作用 返回值类型 方法种类
count 统计个数 long 终结
forEach 逐一处理 void 终结
filter 过滤 Stream 函数拼接
limit 取用前几个 Stream 函数拼接
skip 跳过前几个 Stream 函数拼接
map 映射 Stream 函数拼接
concat 组合 Stream 函数拼接
1、forEach方法(遍历)
java 复制代码
  @Test
    void StreamForEach() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "欧阳无敌", "鱿鱼卷", "苏大娣", "老子", "庄子", "孙子");

        list.stream().forEach(System.out::println);
    }
2、count方法(求个数)
java 复制代码
 @Test
    void StreamCount() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "欧阳无敌", "鱿鱼卷", "苏大娣", "老子", "庄子", "孙子");

        Long a = list.stream().count();
        System.out.println(a);
    }
3、filter方法(过滤)
java 复制代码
@Test
    void StreamFilter() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "欧阳无敌", "鱿鱼卷", "苏大娣", "老子", "庄子", "孙子");

        list.stream().filter(message -> message.length() == 3).forEach(System.out::println);
    }
4、limit方法(只取前几个)
java 复制代码
 @Test
    void StreamLimit() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "欧阳无敌", "鱿鱼卷", "苏大娣", "老子", "庄子", "孙子");

        list.stream().limit(3).forEach(System.out::println);
    }
5、skip方法(跳过前几个)
java 复制代码
 @Test
    void StreamSkip() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "欧阳无敌", "鱿鱼卷", "苏大娣", "老子", "庄子", "孙子");

        list.stream().skip(3).forEach(System.out::println);
    }
6、map方法(映射)
java 复制代码
   @Test
    void StreamMap() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "11", "22", "33", "44", "66", "55");

        list.stream().map(Integer::parseInt).forEach(System.out::println);
    }
7、sorted方法(按元素的自然顺序排列)
java 复制代码
    @Test
    void StreamSorted() {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "11", "22", "33", "44", "66", "55");
        list.stream().sorted().forEach(System.out::println);

        System.out.println("-----------------------------");

        Stream<Integer> integerStream = Stream.of(11, 22, 33, 44, 66, 55);
//        integerStream.sorted().forEach(System.out::println); // 正序
        //通过Comparator构造自定义比较器,
        integerStream.sorted((o1, o2) -> o2 - o1).forEach(System.out::println);// 反序
    }
8、distinct方法(去重)
java 复制代码
  @Test
    void StreamDistinct() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        stream.distinct().forEach(System.out::println);
        System.out.println("-----------------------------");
        Stream<String> stream1 = Stream.of("aa", "bb", "cc", "cc", "dd", "aa");
        stream1.distinct().forEach(System.out::println);
    }
9、distinct方法自定义对象(去重)
java 复制代码
  @Test
    void StreamDistinct1() {
        Stream<Student> stream = Stream.of(
                new Student("西施", 18),
                new Student("貂蝉", 19),
                new Student("貂蝉", 19),
                new Student("杨玉环", 19),
                new Student("王昭君", 20));
        stream.distinct().forEach(System.out::println);
    }
10、allMatch方法(判断所有元素是否满足)
java 复制代码
 @Test
    void StreamAllMatch() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        boolean a = stream.allMatch(message -> message > 7);
        System.out.println(a);
    }
11、anyMatch方法(判断任意元素是否满足)
java 复制代码
@Test
    void StreamAnyMatch() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        boolean a = stream.anyMatch(message -> message > 44);
        System.out.println(a);
    }
12、noneMatch方法(判断所有元素均不满足)
java 复制代码
@Test
    void StreamNoneMatch() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        boolean a = stream.noneMatch(message -> message > 44);
        System.out.println(a);
    }
13、findFirst 和 findAny 方法(查找第一个元素)
java 复制代码
 @Test
    void StreamFindFirst() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        Optional<Integer> a = stream.findFirst();
        System.out.println(a.get());
        System.out.println("---------------------");
        Stream<Integer> stream1 = Stream.of(11, 22, 33, 33, 66, 55);
        Optional<Integer> b = stream1.findAny();
        System.out.println(b.get());
    }
14、max方法(比较最大值,取排列后的最后一个元素)
java 复制代码
 @Test
    void StreamMax() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        Optional<Integer> a = stream.max((o1, o2) -> o1 - o2);
        System.out.println(a.get());
    }
15、min方法(比较最小值,取排列后的第一个元素)
java 复制代码
@Test
    void StreamMin() {
        Stream<Integer> stream = Stream.of(11, 22, 33, 33, 66, 55);
        Optional<Integer> a = stream.min((o1, o2) -> o1 - o2);
        System.out.println(a.get());
    }
16、reduce方法() (各种类型的累积计算操作,例如求和、求积、字符串连接等)
java 复制代码
  @Test
    void StreamReduce() {
        Stream<Integer> stream = Stream.of(4, 5, 3, 9);
//        Integer a = stream.reduce(0,(x,y) -> x+y );
        Optional<Integer> a = stream.reduce((x, y) -> x + y);
        System.out.println(a.get());
    }
17、mapToInt方法 (将Stream中的Integer类型数据转换成int类型)
java 复制代码
  // 同理还有mapLong、mapToDouble
    @Test
    void StreamMapToInt() {
        // Stream<Integer>.filter 自动拆箱进行比较
        Stream<Integer> stream = Stream.of(4, 5, 3, 9).filter(s -> s > 3);
        stream.forEach(System.out::println);
        // 转换成IntStream 减少自动装箱和拆箱
        IntStream intStream = Stream.of(4, 5, 3, 9).mapToInt(Integer::intValue);
        intStream.filter(s1 -> s1 > 3).forEach(System.out::println);
    }
18、collect方法(将流中的数据收集到集合中)
java 复制代码
@Test
    void StreamCollect() {
        Stream<String> stream1 = Stream.of("张三", "李四", "王五", "张三");
        // 收集到List集合中
//        List<String> collect = stream1.collect(Collectors.toList());
//        System.out.println(collect);
        // 收集到Set集合中
//        Set<String> collect = stream1.collect(Collectors.toSet());
//        System.out.println(collect);

        // 收集到指定的集合中AyyayList
//        ArrayList<String> collect = stream1.collect(Collectors.toCollection(ArrayList::new));
//        System.out.println(collect);

        // 收集到指定的集合中AyyayList
        HashSet<String> collect = stream1.collect(Collectors.toCollection(HashSet::new));
        System.out.println(collect);
    }
19、toArray()方法(将流中的数据和收集到数组中)
java 复制代码
@Test
    void StreamToArray() {
        Stream<String> stream1 = Stream.of("张三", "李四", "王五总", "张三");
        // 转成Object数组不方便
//        Object[] array = stream1.toArray();
//        for (Object o : array) {
//            System.out.println(o);
//        }

        String[] array = stream1.toArray(String[]::new);
        for (String s : array) {
            System.out.println(s.length() + s);
        }
    }
20、其他收集流中数据的方式(相当于数据库中的聚合函数)
java 复制代码
 @Test
    public void StreamToOther() {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 58, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 99),
                new Student("柳岩", 52, 77));

        // 获取最大值
        // Optional<Student> max = studentStream.collect(Collectors.maxBy((s1, s2) -> s1.getSocre() - s2.getSocre()));
        // System.out.println("最大值: " + max.get());

        // 获取最小值
        // Optional<Student> min = studentStream.collect(Collectors.minBy((s1, s2) -> s1.getSocre() - s2.getSocre()));
        // System.out.println("最小值: " + min.get());

        // 求总和
        // Integer sum = studentStream.collect(Collectors.summingInt(s -> s.getAge()));
        // System.out.println("总和: " + sum);

        // 平均值
        // Double avg = studentStream.collect(Collectors.averagingInt(s -> s.getSocre()));
        // Double avg = studentStream.collect(Collectors.averagingInt(Student::getSocre));
        // System.out.println("平均值: " + avg);

        // 统计数量
        Long count = studentStream.collect(Collectors.counting());
        System.out.println("统计数量: " + count);

    }
21、groupingBy方法(分组)
java 复制代码
// 
    @Test
    public void StreamGroup() {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33));

        // Map<Integer, List<Student>> map = studentStream.collect(Collectors.groupingBy(Student::getAge));

        // 将分数大于60的分为一组,小于60分成另一组
        Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) -> {
            if (s.getScoure() > 60) {
                return "及格";
            } else {
                return "不及格";
            }
        }));

        map.forEach((k, v) -> {
            System.out.println(k + "::" + v);
        });
    }
22、groupingBy方法(多级分组)
java 复制代码
 @Test
    public void StreamCustomGroup() {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33));

        // 先根据年龄分组,每组中在根据成绩分组
        // groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
        Map<Integer, Map<String, List<Student>>> map = studentStream.collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy((s) -> {
            if (s.getScoure() > 60) {
                return "及格";
            } else {
                return "不及格";
            }
        })));

        // 遍历
        map.forEach((k, v) -> {
            System.out.println(k);
            // v还是一个map,再次遍历
            v.forEach((k2, v2) -> {
                System.out.println("\t" + k2 + " == " + v2);
            });
        });
    }
18、joining方法(拼接)
java 复制代码
// 
    @Test
    public void StreamJoining() {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 99),
                new Student("柳岩", 52, 77));

        // 根据一个字符串拼接: 赵丽颖__杨颖__迪丽热巴__柳岩
        // String names = studentStream.map(Student::getName).collect(Collectors.joining("__"));

        // 根据三个字符串拼接
        String names = studentStream.map(Student::getName).collect(Collectors.joining("__", "^_^", "V_V"));
        System.out.println("names = " + names);
    }
3、Map和reduce的组合使用
java 复制代码
package StreamAPI;
import org.junit.jupiter.api.Test;
import java.util.stream.Stream;

public class StreamMapAndReduceTest {

    // 所有人的年龄总和
    @Test
    void mapAndReduce() {
        Integer allTotalAge = Stream.of(
                new Student("西施", 18),
                new Student("貂蝉", 19),
                new Student("杨玉环", 19),
                new Student("王昭君", 20)).map(Student::getAge).reduce(0, Integer::sum);
        System.out.println(allTotalAge);
    }

    // 找出最大的年龄
    @Test
    void test1() {
        Integer allTotalAge = Stream.of(
                        new Student("西施", 18),
                        new Student("貂蝉", 19),
                        new Student("杨玉环", 19),
                        new Student("王昭君", 20))
                .map(Student::getAge)
                .reduce(0, Math::max); // Integer.max->Math.max
        System.out.println(allTotalAge);
    }

    //统计a的出现次数
    @Test
    void test2() {
//        long number = Stream.of("a","b","c","a","b","a","d").filter((s)->{
//            return s.equals("a");
//        }).count();
        long number = Stream.of("a", "b", "c", "a", "b", "a", "d").map(s -> {
            if (s.equals("a")) {
                return 1;
            } else {
                return 0;
            }
        }).reduce(0, Integer::sum);
        System.out.println(number);
    }
}

3.串行Stream流的获取方式

1、streamParallel方法(直接获取并行的Stream流)
java 复制代码
 @Test
    void Stream_StreamParallel() {
        ArrayList<String> list = new ArrayList<>();
        Stream<String> stream = list.parallelStream();
    }
2、parallel方法(将串行流转换成并行流)
java 复制代码
 @Test
    void StreamParallel() {
        long count = Stream.of(4, 5, 3, 9, 1, 2, 6)
                .parallel() // 转成并行流
                .filter(s -> {
                    System.out.println(Thread.currentThread() + "::" + s);
                    return s > 3;
                })
                .count();
        System.out.println(count);
    }
3、解决parallelStream线程安全问题的三个方案
java 复制代码
package StreamAPI;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

public class StreamParallelTest {

    private static final int times = 500000000;
    long start;

    @BeforeEach
    public void init() {
        start = System.currentTimeMillis();
    }

    @AfterEach
    public void destory() {
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end - start));
    }

    // 并行的Stream : 消耗时间:137
    @Test
    public void testParallelStream() {
        LongStream.rangeClosed(0, times).parallel().reduce(0, Long::sum);
    }

    // 串行的Stream : 消耗时间:343
    @Test
    public void testStream() {
        // 得到5亿个数字,并求和
        LongStream.rangeClosed(0, times).reduce(0, Long::sum);
    }

    // 使用for循环 : 消耗时间:235
    @Test
    public void testFor() {
        int sum = 0;
        for (int i = 0; i < times; i++) {
            sum += i;
        }
    }


    // parallelStream线程安全问题
    @Test
    void test1() {
        ArrayList<Integer> list = new ArrayList<>();
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    list.add(i);
                });
        System.out.println("list = " + list.size()); // list.size < 1000
    }

    // 解决parallelStream线程安全问题方案一: 使用同步代码块
    @Test
    public void test2() {
        ArrayList<Integer> list = new ArrayList<>();
        Object obj = new Object();
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronized (obj) {
                        list.add(i);
                    }
                });
    }

    // 解决parallelStream线程安全问题方案二: 使用线程安全的集合
    @Test
    public void test3() {
        ArrayList<Integer> list = new ArrayList<>();
         Vector<Integer> v = new Vector();
        List<Integer> synchronizedList = Collections.synchronizedList(list);
        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronizedList.add(i);
                });
        System.out.println("list = " + synchronizedList.size());
    }

    // 解决parallelStream线程安全问题方案三: 调用Stream流的collect/toArray
    @Test
    public void test4() {
        ArrayList<Integer> list = new ArrayList<>();
        List<Integer> collect = IntStream.rangeClosed(1, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println("collect.size = "+collect.size());
    }
}

四、parallelStream背后的技术

1、Fork/Join框架介绍

parallelStream使用的是Fork/Join框架。Fork/Join框架自JDK 7引入。Fork/Join框架可以将一个大任务拆分为很多小 任务来异步执行。 Fork/Join框架主要包含三个模块:

  1. 线程池:ForkJoinPool
  2. 任务对象:ForkJoinTask
  3. 执行任务的线程:ForkJoinWorkerThread

2、Fork/Join原理-分治法

把大任务拆成小任务

Fork/Join案例:

需求:使用Fork/Join计算1-10000的和,当一个任务的计算数量大于3000时拆分任务,数量小于3000时计算。

java 复制代码
package study;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class ForkJoinTest {

    public static void main(String[] args) {
        Long startTime = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        SumRecursiveTask sumRecursiveTask = new SumRecursiveTask(1, 999999999);
        Long result = pool.invoke(sumRecursiveTask);
        System.out.println("result = " + result);
        Long endTime = System.currentTimeMillis();
        System.out.println("消耗时间 = " + (endTime - startTime));
    }




}

// 创建一个求和的任务
// RecursiveTask:一个任务
class SumRecursiveTask extends RecursiveTask<Long> {
    // 是否要拆分的临界值
    private static final long THRESHOLD = 3000L;
    // 起始值
    private final long start;
    // 结束值
    private final long end;

    SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long lenth = end - start;
        if (lenth < THRESHOLD){
            // 小于拆分临界值
            long sum = 0;
            for (long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            // 拆分
            long middle = (start + end) / 2;
            SumRecursiveTask left = new SumRecursiveTask(start, middle);
            left.fork();
            SumRecursiveTask right = new SumRecursiveTask(middle + 1, end);
            right.fork();
            return left.join() + right.join();
        }
    }
}

3、Fork/Join原理-工作窃取算法

Fork/Join最核心的地方就是利用了现代硬件设备多核,在一个操作时候会有空闲的cpu,那么如何利用好这个空闲的 cpu就成了提高性能的关键,而这里我们要提到的工作窃取(work-stealing)算法就是整个Fork/Join框架的核心理念 Fork/Join工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。

4、小结和注意事项

  1. parallelStream是线程不安全的
  2. parallelStream适用的场景是CPU密集型的,只是做到别浪费CPU,假如本身电脑CPU的负载很大,那还到处用 并行流,那并不能起到作用
  3. I/O密集型 磁盘I/O、网络I/O都属于I/O操作,这部分操作是较少消耗CPU资源,一般并行流中不适用于I/O密集 型的操作,就比如使用并流行进行大批量的消息推送,涉及到了大量I/O,使用并行流反而慢了很多
  4. 在使用并行流的时候是无法保证元素的顺序的,也就是即使你用了同步集合也只能保证元素都正确但无法保证 其中的顺序

五、Optional类

1、Optional类介绍

Optional是一个没有子类的工具类,Optional是一个可以为null的容器对象。它的作用主要就是为了解决避免Null检 查,防止NullPointerException。

2、Optional的常用方法基本使用

方法名 作用
empty() 静态方法,返回一个空的Optional实例。
of(T value) 静态方法,返回一个包含指定值的Optional实例,如果指定值为null,则抛出NullPointerException。
ofNullable(T value) 静态方法,返回一个Optional实例,若传入值为null,则返回一个空的Optional实例。
filter(Predicate<? super T> predicate) 如果值存在并且满足提供的谓词,返回表示该值的Optional;否则返回一个空的Optional。
isPresent() 如果值存在,返回true;否则返回false。
ifPresent(Consumer<? super T> consumer) 如果值存在,执行指定的操作。
orElse(T other) 如果值存在,返回该值;否则返回指定的其他值。
orElseGet(Supplier<? extends T> other) 如果值存在,返回该值;否则使用指定的Supplier函数生成一个值返回。
orElseThrow(Supplier<? extends X> exceptionSupplier) 如果值存在,返回该值;否则抛出由Supplier函数生成的异常。
java 复制代码
package study;

import java.util.Optional;

public class study {
    public static void main(String[] args) {
        test();
        test1();
        test2();
        test3();
        test4();

    }

    // 静态of方法(具体值)
    public static void test() {
        Optional<String> op1 = Optional.of("凤姐");
//        Optional<String> op2 = Optional.of(null); // NullPointerException
    }

    // ofNullable(具体值/空)
    public static void test1() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        Optional<Object> op4 = Optional.ofNullable(null);
    }

    // empty(只能传入空)
    public static void test2() {
        Optional<Object> op3 = Optional.empty();

    }

    // 判断Optional是否有具体值
    // 1.isPresent()
    public static void test3() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        System.out.println(op3.isPresent()); // true
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op4.isPresent()); // false
    }

    // 1.isPresent()
    public static void test4() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        System.out.println(op3.get()); // 如花
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op4.get()); // NoSuchElementException
    }


}
|

| orElseGet(Supplier<? extends T> other) | 如果值存在,返回该值;否则使用指定的Supplier函数生成一个值返回。 |

| orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果值存在,返回该值;否则抛出由Supplier函数生成的异常。 |

java 复制代码
package study;

import java.util.Optional;

public class study {
    public static void main(String[] args) {
        test();
        test1();
        test2();
        test3();
        test4();

    }

    // 静态of方法(具体值)
    public static void test() {
        Optional<String> op1 = Optional.of("凤姐");
//        Optional<String> op2 = Optional.of(null); // NullPointerException
    }

    // ofNullable(具体值/空)
    public static void test1() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        Optional<Object> op4 = Optional.ofNullable(null);
    }

    // empty(只能传入空)
    public static void test2() {
        Optional<Object> op3 = Optional.empty();

    }

    // 判断Optional是否有具体值
    // 1.isPresent()
    public static void test3() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        System.out.println(op3.isPresent()); // true
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op4.isPresent()); // false
    }

    // 1.isPresent()
    public static void test4() {
        Optional<Object> op3 = Optional.ofNullable("如花");
        System.out.println(op3.get()); // 如花
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op4.get()); // NoSuchElementException
    }


}
相关推荐
Daniel 大东13 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea
wind瑞19 分钟前
IntelliJ IDEA插件开发-代码补全插件入门开发
java·ide·intellij-idea
HappyAcmen20 分钟前
IDEA部署AI代写插件
java·人工智能·intellij-idea
马剑威(威哥爱编程)25 分钟前
读写锁分离设计模式详解
java·设计模式·java-ee
鸽鸽程序猿26 分钟前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
修道-032327 分钟前
【JAVA】二、设计模式之策略模式
java·设计模式·策略模式
九圣残炎32 分钟前
【从零开始的LeetCode-算法】2559. 统计范围内的元音字符串数
java·算法·leetcode
Shepherd061943 分钟前
【Jenkins实战】Windows安装服务启动失败
运维·jenkins
当归10241 小时前
若依项目-结构解读
java
hlsd#1 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端