JDK8新特性(一)集合之 Stream 流式操作

news2024/11/16 6:04:23

1.Stream流由来

       首先我们应该知道:Stream流的出现,主要是用在集合的操作上。在我们日常的工作中,经常需要对集合中的元素进行相关操作。诸如:增加、删除、获取元素、遍历。

        最典型的就是集合遍历了。接下来我们先举个例子来看看 JDK8 Stream流式操作出现之前,我们对集合操作的过程,从中来了解一下 JDK8 之前集合操作数据的弊端。

     Demo:现在有一个List集合,集合中有如下数据:"张无忌"、"周芷若"、"杨逍"、"张强"、"张三丰"、"赵敏"

    public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            Collections.addAll(list,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
            /**
             * 需求:
             *   1.拿到所有姓"张"的名字
             *   2.拿到长度为3个字的名字
             *   3.将最终结果进行打印
             */
            //JDK8 以前遍历操作集合
            for (String name : list) {
                if(name.startsWith("张") && name.length() == 3){
                    System.out.println(name);
                }
            }
            //JDK8 以后使用Stream()流遍历操作集合
            /**************3.使用 Stream流来操作******************/
            list.stream()
                    .filter(name.startsWith("张") && name->name.length()==3)
                    .forEach(name-> System.out.println("111------"+name));
        }

       使用stream流式操作,直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:①获取流  ② 过滤姓张  ③过滤长度为3  ④遍历打印。我们真正要做的事情内容便能够被更好的体现在代码中。

2.Stream流式思想

  注意:Stream流 和 IO 流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象。

       Stream流式思想类似于工厂车间的"生产流水线",Stream流不是一种数据结构,不会保存数据,而是对数据进行加工处理。Stream 可以看做是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

3.获取Stream流的两种方式 

java.util.stream.Stream<T> 是 JDK8 新加入的流接口。获取一个流非常简单,有以下两种常用的方式:

1.所有的 Collection 集合都可以通过 .stream() 方法来获取流;
2.使用 Stream 接口的 .of() 静态方法,可以获取流。
    public static void main(String[] args) {
        //方式1:根据Collection获取流
        //Collection接口中有一个默认的方法:default Stream<E> stream()
        //1.List获取流
        List<String> list = new ArrayList<>();
        Stream<String> stream01 = list.stream();

        //2.Set获取流
        Set<String> set = new HashSet<>();
        Stream<String> stream02 = set.stream();

        //3.Map获取流
        //Map 并没有继承自 Collection 接口,所有无法通过该 map.stream()获取流。但是可用通过如下三种方式获取:
        Map<String,String> map = new HashMap<>();
        Stream<String> stream03 = map.keySet().stream();
        Stream<String> stream04 = map.values().stream();
        List<Map.Entry<String, String>> collect = map.entrySet().stream().collect(Collectors.toList());
        Stream<Map.Entry<String, String>> stream = map.entrySet().stream();
        //方式2:Stream中的静态方法of获取流
        // static<T> Stream<T> of(T... values)
        // T... values:可变参数,实际原理就是可变数组(传递String数组进去)

        //1.字符串获取流
        Stream<String> stream06 = Stream.of("aa", "bb", "cc");

        //2.数组类型(基本类型除外)
        String[] strs = {"aa","bb","cc"};
        Stream<String> stream07 = Stream.of(strs);

        //3.基本数据类型的数组
        int[] arr = {1,2,3,4};
        //看着没报错,但是看到返回值是 int[],这是 Stream流把整个数组看做一个元素来操作,而不是操作数组中的int元素(这样子是不行的!!!)
        Stream<int[]> stream08 = Stream.of(arr);
        }

4.Stream流常用方法

      Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种类型:

  •  终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count() forEach() 方法;
  • 非终结方法:又叫函数拼接方法。值返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其与方法均为非终结方法)

5.Stream流使用注意事项

Stream流只能操作一次;
Stream方法返回的是新的流;
Stream不调用终止方法,中间的操作不会执行。

6.Stream流常用方法 

   提醒以下所有代码部分,能简化部分尽量简化,均使用最简格式!!! 

 1.forEach 

void forEach(Consumer<? super T> action);

        forEach() 方法用来遍历流中的数据,是一个终结方法。该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。示例如下:

public class StreamDemo{
 
    public static void main(String[] args){
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");
        //forEach()遍历
        //未简写
        //list.forEach((String str)->{
        //    System.out.println(str);
        //});
        //简写1
        //list.forEach(str-> System.out.println(str));
        //最终简写
        list.forEach(System.out::println);
    }
}

测试结果: 

2.count 

 long count();

 count() 方法,用来统计集合中的元素个数,是一个终结方法。该方法返回一个 long 值代表元素个数,示例如下:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "Mary", "Lucy", "James", "Johson", "Steve");
        //count()计算集合中元素个数
        long count = list.stream().count();
        System.out.println("元素个数为:" + count);
    }

 

3.filter 

Stream<T> filter(Predicate<? super T> predicate);

filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。我们可以通过 filter() 方法将一个流转换成另一个子集流。该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件。

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");
        //filter()过滤,返回以"J"开头的名字
        list.stream().filter(str->str.startsWith("J"))
                .forEach(System.out::println);

        //写法二
        list.stream().filter(str->{
            if (str.startsWith("J")){
                return true;
            }
            return false;
        }).forEach(System.out::println);
    }

  测试结果:

4.limit

Stream<T> limit(long maxSize);

       limit() 方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。因为 limit() 是一个非终结方法,所以必须调用终止方法。示例如下:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "Mary", "Lucy", "James", "Johson", "Steve");
        //limit()截取,截取list集合前三个元素
        list.stream().limit(3).forEach(System.out::println);
        
    }

 5.skip

      如果希望跳过前几个元素,去取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流,它是一个非终结方法。参数是一个 long 型,如果 Stream 流的当前长度大于 n,则跳过前 n 个,否则将会得到一个长度为 0 的空流。因为 limit() 是一个非终结方法,所以必须调用终止方法。示例如下:

Stream<T> skip(long n);
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");
        //skip()跳过list集合前2个元素,获取剩下的元素
        list.stream().skip(2).forEach(System.out::println);
    }

 备注:使用 skip() 和 limit() 方法,即可实现类似分页的操作了。示例如下:

    //一页10条 分页操作
    //第一页
    skip(0).limit(10)
    //第二页
    skip(10).limit(10)
    //第三页
    skip(20).limit(10)
    ...

6.map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

      map() 方法,可以将流中的元素映射到另一个流中。即:可以将一种类型的流转换为另一种类型的流(区别:map返回的是指定类型(比如int),而flatMap返回的还是一个Stream流),map() 方法是一个非终结方法。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

       这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong 和 mapToDouble。这三个方法也比较好理解,比如 mapToInt 就是把原始 Stream 转换成一个新的 Stream,这个新生成的 Stream 中的元素都是 int 类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。(参考:本文 15 mapToInt/mapToLong/mapToDouble)

       因为 map() 方法是一个非终结方法,所以必须调用终止方法。通过如下示例,使用 map() 方法,通过方法引用,便将字符串类型转换成了 Integer 类型。示例如下:

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "11", "22", "33", "44", "55");
        //通过map()方法,可以将String类型的流转换为int类型的流
        /*list.stream().map((String str)->{
              return Integer.parseInt(str);
          }).forEach((Integer num) -> {
              System.out.println(num);
          });*/
        //简化:
        //list.stream().map(str->Integer.parseInt(str)).forEach(str->System.out.println(str));
        //简化后:
        list.stream().map(Integer::parseInt).forEach(System.out::println);
    }

 7.flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

flatMap 的使用,同 map 类似。map只是一维 1对1 的映射,返回的是指定的类型;

      而flatMap返回的则还是一个Stream流,可以对其进行进一步操作。(区别:map返回的是指定类型(比如int),而flatMap返回的还是一个Stream流)

      我的理解为:假如你的集合流中包含子集合(或者需要更深进一步操作),那么使用 flatMap 可以返回该子集合的集合流。示例代码如下所示:

public class Province {
 
    private String name;
 
    private List<String> city;
 
    //get/set 方法
}
 
public class flatMapDemo{
    public static void main(String[] args) {
 
        List<Province> provinceList = new ArrayList<>();
 
        List<String> bjCityList = new ArrayList<>();
        bjCityList.add("海淀");
        bjCityList.add("朝阳");
 
        List<String> shCityList = new ArrayList<>();
        shCityList.add("黄埔");
        shCityList.add("闵行");
 
        Province bjProvince = new Province();
        bjProvince.setName("北京");
        bjProvince.setCity(bjCityList);
        provinceList.add(bjProvince);
 
        Province shProvince = new Province();
        shProvince.setName("上海");
        shProvince.setCity(shCityList);
        provinceList.add(shProvince);
 
        //使用map,需要多次forEach
        provinceList.stream().map(str->str.getCity()).forEach(cityList -> cityList.forEach(System.out::println));
 
        System.out.println("----------");
 
        //使用 flatMap
        provinceList.stream().flatMap(str->str.getCity().stream()).forEach(System.out::println);
    }
}

 测试结果:

8.sorted 

sorted() 方法,可以用来对 Stream 流中的数据进行排序。sorted()方法共有以上两种情况:

    //根据元素的自然规律排序
    Stream<T> sorted();
    //根据比较器指定的规则排序
    Stream<T> sorted(Comparator<? super T> comparator);

       因为 sorted() 方法是一个非终结方法,所以必须调用终止方法。

       场景:①sorted() 方法:按照自然规律,默认为升序排序。

                  ②sorted(Comparator comparator)  方法,按照指定的比较器规则排序(以降序为例)。 示例如下:

    public static void main(String[] args){
        //sorted():根据元素的自然规律排序
        Stream<Integer> stream01 = Stream.of(66,33,11,55);
        stream01 .sorted().forEach(System.out::println);
        System.out.println("   -----  ");
        //sorted(Comparator<? super T> comparator):根据比较器规则降序排序
        Stream<Integer> stream02 = Stream.of(66,33,11,55);
        stream02 .sorted((i1,i2)-> i2-i1).forEach(System.out::println);
    }

9.distinct 

distinct() 方法,可以用来去除重复数据。因为 distinct() 方法是一个非终结方法,所以必须调用终止方法。

       去除重复数据,此处有几种情况:①基本类型去重   ②String类型去重    ③引用类型去重(对象去重)

       ①②使用 distinct() 方法可以直接去重  ③对象类型需要重写 equals() 和 hasCode() 方法,使用 distinct() 方法才能去重成功。示例如下:

public static void main(String[] args){
        //基本类型去重
        Stream<Integer> stream01 = Stream.of(66,33,11,55,33,22,55,66);
        stream01 .distinct().forEach(System.out::println);
 
        //字符串去重
        Stream<String> stream02 = Stream.of("AA","BB","AA");
        stream02.distinct().forEach(System.out::println);
 
        //自定义对象去重
        //(Person对象类,有String name,Integer age 两个属性,两参数构造器,get/set()方法,重写了equals(),hashCode(),toString()方法。此处就不附Person实体类了)
        BiFunction<String,Integer,Person> fn1 = Person::new;
        Stream<Person> stream14 = Stream.of(fn1.apply("西施", 18), fn1.apply("貂蝉", 20), fn1.apply("王昭君", 22), fn1.apply("杨玉环", 23), fn1.apply("杨玉环", 23));
        stream14.distinct().forEach(System.out::println);
    }

 测试结果:

    66
    33
    11
    55
    22
    ----
    AA
    BB
    ----
    Person{name='西施', age=18}
    Person{name='貂蝉', age=20}
    Person{name='王昭君', age=22}
    Person{name='杨玉环', age=23}

10.match

//allMatch 全匹配(匹配所有,所有元素都需要满足条件-->返回true)
boolean allMatch(Predicate<? super T> predicate);
//anyMatch 匹配某个元素(只要有一个元素满足条件即可-->返回true)
boolean anyMatch(Predicate<? super T> predicate);
//noneMatch 匹配所有元素(所有元素都不满足指定条件-->返回true)
boolean noneMatch(Predicate<? super T> predicate);

       match() 方法,可以用来判断 Stream 流中的数据是否匹配指定的条件。allMatch()、anyMatch()、noneMatch() 方法都是终结方法,返回值为 bollean。示例如下:

    public static void main(String[] args){
        Stream<Integer> stream01 = Stream.of(5, 3, 6, 1);
        boolean allMatch = stream01.allMatch(i -> i > 0);
        System.out.println("allMatch匹配:"+allMatch);

        Stream<Integer> stream02 = Stream.of(5, 3, 6, 1);
        boolean anyMatch = stream02 .anyMatch(i -> i > 5);
        System.out.println("anyMatch匹配:"+anyMatch);

        Stream<Integer> stream03 = Stream.of(5, 3, 6, 1);
        boolean noneMatch = stream03 .noneMatch(i -> i < 3);
        System.out.println("noneMatch匹配:"+noneMatch);
    }

11.max / min 

    Optional<T> max(Comparator<? super T> comparator);
    Optional<T> min(Comparator<? super T> comparator);

       max() 和 min() 方法,用来获取 Stream 流中的最大值和最小值。该接口需要一个 Comparator 函数式接口参数,根据指定排序规则来获取最大值,最小值。

       为了保证数据的准确性,此处排序规则需要是升序排序。因为:max() 方法获取的是排序后的最后一个值,min() 方法获取的是排序后的第一个值。如果使用降序排序后,那么 max() 和 min() 方法就相反了,就有异常了。示例如下:

    public static void main(String[] args){
        //max()
        Stream<Integer> stream01 = Stream.of(33, 11, 22, 5);
        Optional<Integer> max = stream01.max((i1, i2) -> i1 - i2);
        System.out.println("最大值:"+max.get());

        //min()
        Stream<Integer> stream02 = Stream.of(33, 11, 22, 5);
        Optional<Integer> min = stream02.min((i1, i2) -> i1 - i2);
        System.out.println("最小值:"+min.get());
    }

测试结果:

12.reduce 

//1.
Optional<T> reduce(BinaryOperator<T> accumulator);
//2.
T reduce(T identity, BinaryOperator<T> accumulator);
//3.
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

     如果需要将 Sream 流中的所有数据,归纳得到一个数据的情况,可以使用 reduce() 方法。如果需要对 Stream 流中的数据进行求和操作、求最大/最小值等(都是归纳为一个数据的情况),此处就可以用到 reduce() 方法。示例如下

    public static void main(String[] args){
        //reduce():求和操作
        Stream<Integer> stream01 = Stream.of(4,3,5,6);
        Integer sum = stream01.reduce(0,(x,y)-> x + y);
        System.out.println("求和:"+sum);

        //reduce():求最大值操作
        Stream<Integer> stream03 = Stream.of(4,3,5,6);
        Integer max= stream03.reduce(0,(x,y)-> x > y ? x : y);
        System.out.println("最大值为:"+max);

        //reduce():求最小值操作
        Stream<Integer> stream02 = Stream.of(4,3,5,6);
        Optional<Integer> max1= stream02.reduce((x, y)-> x < y ? x : y);
        System.out.println("最小值为:"+max1.get());
    }

 测试结果: 

 结果分析:(求和分析) 

  1. 求和流程:

  2. 第一次:将默认值赋值给x,取出集合第一个元素赋值给y

  3. 第二步:将上一次返回的结果赋值给x,取出集合第二个元素赋值给y

  4. 第三步:继续执行第二步(如下图所示)

13.map 和 reduce 方法组合使用 

    map() 和 reduce() 方法组合使用,可以解决很多日常工作中遇到的问题。我们就从如下场景了解:

       场景一:现在有一个 Person 类,有两个属性:name 和 age,新建四个 Person类,然后完成如下操作:① 求出所有年龄的总和   ②求出 Person 类中的最大年龄

    public static void main(String[] args){
        BiFunction<String,Integer,Person> fn2 = Person::new;

        //1.求出所有年龄的总和(年龄为int值,默认为0,此处可以使用待默认值的reduce()方法)
        Stream<Person> stream01 = Stream.of(fn2.apply("刘德华", 58), fn2.apply("张学友", 56), fn2.apply("郭富城", 54), fn2.apply("黎明", 52));
        //基本写法:
        //Integer total = stream01.map(p -> p.getAge()).reduce(0,(x, y) -> x + y);
        //(方法引用)简化后:
        Integer total = stream01.map(p -> p.getAge()).reduce(0,Integer::sum);
        System.out.println("年龄总和为:"+total);

        //2.找出最大年龄
        Stream<Person> stream02 = Stream.of(fn2.apply("刘德华", 58), fn2.apply("张学友", 56), fn2.apply("郭富城", 54), fn2.apply("黎明", 52));
        //基本写法:        
        //Integer maxAge = stream02.map(p -> p.getAge()).reduce(0, (x, y) -> x > y ? x : y);
        //(方法引用)简化后:
        Integer maxAge = stream02.map(p -> p.getAge()).reduce(0, Integer::max);
        System.out.println("最大年龄为:"+maxAge);
    }

 测试结果 

    年龄总和为:220
    最大年龄为:58

 场景二:统计字符串 "a" 出现的次数

    public static void main(String[] args){
        Stream<String> stream03 = Stream.of("a", "b", "c", "d", "a", "c", "b", "a");
        //map() 和 reduce() 方法组合使用
        Integer aTotal = stream03.map(str -> {
            if (str == "a") {
                return 1;
            } else {
                return 0;
            }
        }).reduce(0, Integer::sum);
        System.out.println("a次数:"+aTotal);
    }

 14.mapToInt / mapToDouble / mapToLong

//mapToInt()
IntStream mapToInt(ToIntFunction<? super T> mapper);
//mapToLong()
LongStream mapToLong(ToLongFunction<? super T> mapper);
//mapToDouble()
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

  我们通过 Stream<Integer>  stream = Stream.of(1,2,3,4,5); 这种方式,返回值为 Stream<Integer> 这种包装类的泛型,这种方式虽然用起来没有问题,但是它在效率上还是存在着一定的问题。

       当我们将一对数字转成 Stream 流时,因为泛型的原因,只能使用 Integer 包装类。会先把这些数字包装成 Integer 类

//1.Integer是一个类,占用的内存肯定比 int 大
//2.Stream流在操作时,会存在自动装箱和拆箱操作
Stream<Integer> stream = Stream.of(2,3,5,6,7);
 
//把大于3的打印出来(num在Stream流中是Integer类型,在与3比较时,显然会存在自动拆装箱问题),效率会有影响
stream.filter(num -> num > 3).forEach(System.out::println);

       所以在 JDK8 中,对 Stream 流还新增了一个 mapToInt()方法。该方法可以将流中操作的 Integer 包装类,在 Stream 流中转换成直接来操作 int 类型,效率明显会高一点。

示例如下:

    public static void main(String[] args){
        //使用 mapToInt()方法
        IntStream intStream = Stream.of(1, 2, 3, 4, 5, 6).mapToInt((Integer num) -> {
            return num.intValue();
        });

        //(使用方法引用)简化后
        IntStream intStream1 = Stream.of(1, 2, 3, 4, 5, 6).mapToInt(Integer::intValue);

        intStream1.filter(n->n>3).forEach(System.out::println);
        /**
         * 使用mapToInt(),返回值是一个IntStream类型.我们看一下它们的继承结构图(如下所示):
         * IntStream 和 Stream<Integer> 类型进行比较。发现他们都继承自 BaseStream。所以它们区别不大
         * 只不过 IntStream 内部操作的是 int 基本类型的数据,省去自动拆装箱过程。从而可以节省内存开销
         */
    }

 继承结构图:

提示:

        mapToDouble() / mapToLong() 的使用,与 mapToInt()一致,此处不再介绍。

15.concat 

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流合并成一个流进行返回。如果是三个流,则需要两两合并,不能一次性合并三个流。concat() 方法是 Stream 接口的静态方法,我们可以直接使用类名.方法名】调用。

注意:concat() 方法此处接收的是 Stream 类型,不能接收 IntStream 等类型。concat() 是一个静态方法,与 java.lang.String 中的 concat() 方法是不同的。

    public static void main(String[] args){
        //concat()方法
        Stream<Integer> aStream = Stream.of(1, 2, 3);
        Stream<Integer> bStream = Stream.of(4, 5, 6);

        Stream<Integer> concatStream = Stream.concat(aStream, bStream);
        concatStream.forEach(System.out::println);
    }

 测试结果:

16.peek 

介绍:该方法会生成一个包含原 Stream 的所有元素的新 Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数 

存在此方法的目的,主要是为了在您需要的地方支持调试,查看元素流过管道中特定点的情况

主要用于开发过程中调试使用!!!

    public static void main(String[] args){
        List<String> list = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("大于三---" + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("转大写---" + e))
                .collect(Collectors.toList());
    }

 17.allMatch / anyMatch / noneMatch 匹配相关

这三个方法,均返回 boolean 类型 

allMatch:是不是Stream中的所有元素都满足给定的匹配条件
anyMatch:Stream中是否存在任何一个元素满足匹配条件
noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件
    public static void main(String[] args) {
// Stream中元素,所有元素长度都>2
        boolean flag = Stream.of("one", "two", "three", "four").allMatch(str -> str.length() > 2);
        System.out.println(flag);     // 返回值:true

// Stream中元素,是不是存在任何一个元素长度>4
        boolean flag1 = Stream.of("one", "two", "three", "four").anyMatch(str -> str.length() > 4);
        System.out.println(flag1);     // 返回值:true

// Stream中元素,所有元素长度没有一个元素<1
        boolean flag2 = Stream.of("one", "two", "three", "four").noneMatch(str -> str.length() < 1);
        System.out.println(flag2);     // 返回值:true
    }

 

18.findFirst / findAny 

findFirst:返回Stream中的第一个元素,如果Stream为空,返回空Optional
findAny:返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个
    public static void main(String[] args) {
        Optional<String> first = Stream.of("one", "two", "three", "four").findFirst();
        System.out.println(first.get());

        Optional<String> any = Stream.of("one", "two", "three", "four").findAny();
        System.out.println(any.get());

        Optional<String> any1 = Stream.of("one", "two", "three", "four").parallel().findAny();
        System.out.println(any1.get());
        
    }

 

 19.collect

collect() 方法的使用,也有很多内容学习,此处内容过多,不做一一列举。

       如需了解 Stream 流 collect() 方法的使用介绍,你可以看博主下一文章学习了解。请点击如下链接跳转:JDK8新特性(二):Stream流 collect() 方法的详细使用介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1404084.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8.前端--CSS-显示模式

元素的显示模式 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;比如<div>自己占一行&#xff0c;比如一行可以放多个<span>。 1.块元素 常见的块元素 常见的块元素&#xff1a;<h1>~<h6>、<p>、<div>、…

sqlmap使用教程(2)-连接目标

目录 连接目标 1.1 设置认证信息 1.2 配置代理 1.3 Tor匿名网络 1.4 检测WAF/IPS 1.5 调整连接选项 1.6 处理连接错误 连接目标 场景1&#xff1a;通过代理网络上网&#xff0c;需要进行相应配置才可以成功访问目标主机 场景2&#xff1a;目标网站需要进行身份认证后才…

【立创EDA-PCB设计基础】5.布线设计规则设置

前言&#xff1a;本文详解布线前的设计规则设置。经过本专栏中的【立创EDA-PCB设计基础】前几节已经完成了布局&#xff0c;接下来开始进行布线&#xff0c;在布线之前&#xff0c;要设置设计规则。 目录 1.间距设置 1.1 安全间距设置 1.2 其它间距设置 2.物理设置 2.1 导…

Oracle Linux 8.9 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;本次安装图解是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

nodejs前端项目的CI/CD实现(二)jenkins的容器化部署

一、背景 docker安装jenkins&#xff0c;可能你会反问&#xff0c;这太简单了&#xff0c;有什么好讲的。 我最近就接手了一个打包项目&#xff0c;它是一个nodejs的前端项目&#xff0c;jenkins已在容器里部署且运行OK。 但是&#xff0c;前端组很追求新技术&#xff0c;不…

联邦学习:密码学 + 机器学习 + 分布式 实现隐私计算,破解医学界数据孤岛的长期难题

联邦学习&#xff1a;密码学 机器学习 分布式 提出背景&#xff1a;数据不出本地&#xff0c;又能合力干大事联邦学习的问题联邦学习架构 分布式机器学习&#xff1a;解决大数据量处理的问题横向联邦学习&#xff1a;解决跨多个数据源学习的问题纵向联邦学习&#xff1a;解决…

2023年12月青少年机器人技术等级考试(六级)理论综合试卷

2023年12月青少年机器人技术等级考试&#xff08;六级&#xff09;理论综合试卷 单选题 第 1 题 单选题 IPv6地址长度及其地址分隔符分别是&#xff1f;&#xff08; &#xff09; A.32 . B.128 . C.32 : D.128 : 第 2 题 单选题 浏览网页时&#xff0c;使用应用层的…

verde生成网格坐标

文章目录 网格坐标区域调整 Verde是Python用于地理空间数据处理的一个库&#xff0c;由于采用了一些机器学习的方法&#xff0c;所以除了科学计算三件套之外&#xff0c;还需要基于sklearn模块。考虑到依赖关系&#xff0c;这里比较推荐用conda安装。 conda install verde --c…

第二节 K8S 的架构

第二节 K8S 的架构 K8S 架构图如下: 官方文档: https://kubernetes.io/docs/concepts/architecture/ kube-api-server 是集群的核心&#xff0c; 是k8s中最重要的组件&#xff0c; 因为它是实现声明式api的关键, 整个集群的入口,所有请求都要经过它, api接口服务. kubernetes…

Linux 快速构造大数据文件

文章目录如下 1. 如何生成数据文件 2. 使用 yes 命令构造数据 2.1. 基本用法 2.2. 构造数据文件 3. 使用 awk 命令构造数据 3.1. 基本用法 3.2. awk 循环输出 3.3. awk 指定分隔符 3.4. awk 随机数 3.5. awk 随机字符 3.6. awk 构造数据 4. 总结 1. 如何生成数据文…

【剑指offer】重建二叉树

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述1、题目2、示例 二、题目分析1、递归2、栈 一、题目描述 1、题目 剑指offer&#xff1a;重建二叉树 给定节…

opencv#27模板匹配

图像模板匹配原理 例如给定一张图片&#xff0c;如上图大矩阵所示&#xff0c;然后给定一张模板图像&#xff0c;如上图小矩阵。 我们在大图像中去搜索与小图像中相同的部分或者是最为相似的内容。比如我们在图像中以灰色区域给出一个与模板图像尺寸大小一致的区域&#xff0c;…

LeetCode 48 旋转图像

题目描述 旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4…

Java-泛型与通配符

泛型基础 泛型示例 泛型的作用 泛型的语法 注意事项 1.泛型只能用引用类型实例化而非基本类型 自定义泛型 自定义泛型类 2中数组不能初始化是因为不知道要开辟多大的空间 3 类加载是会加载静态方法 此时泛型的具体类型并没有被确定 因为对象没有被 声明或实例化 自定义泛…

linux的安装配置

文章目录 1.centos7安装2.如何进行一个网络的开启3.客户端Xshell和Xftp的一个使用4.换源 1.centos7安装 1.我是在虚拟机里面重装了一个liunx系统,首先我们新建一个虚拟机 2.前面东西都不需要我们进行一个选择&#xff0c;到图中的这一步我们选择一个liunx,版本的话我们选择一个…

读AI3.0笔记04_视觉识别

1. 两次飞跃 1.1. ConvNets是当今计算机视觉领域深度学习革命的驱动力 1.1.1. 20世纪80年代便由法国计算机科学家杨立昆提出&#xff0c;而他则是受到了福岛邦彦提出的神经认知机&#xff08;Neocognitron&#xff09;的启发 1.2. ImageNet竞赛被看作计算机视觉和人工智能进…

zero w配置C++ opencv csi 摄像头

经过一天半的摸索&#xff0c;踩过了很多坑&#xff0c;这里记录一下 1、系统网站&#xff1a;https://www.raspberrypi.org/downloads/raspberry-pi-os/ 由于zero w 性能不太行&#xff0c;我下载的是&#xff1a; 2、系统烧录&#xff1a; 这里的坑有两个&#xff0c;一个…

【深度学习】BasicSR训练过程记录,如何使用BasicSR训练GAN

文章目录 两种灵活的使用场景项目结构概览简化的使用方式 项目结构解读1. 代码的入口和训练的准备工作2. data和model的创建2.1 dataloader创建2.2 model的创建 3. 训练过程 动态实例化的历史演进1. If-else判断2. 动态实例化3. REGISTER注册机制 REGISTER注册机制的实现1. DAT…

React Router v6 改变页面Title

先说正事再闲聊 1、在路由表加个title字段 2、在index包裹路由 3、在App设置title 闲聊&#xff1a; 看到小黄波浪线了没 就是说默认不支持title字段了 出来的提示&#xff0c; 所以我本来是像下面这样搞的&#xff0c;就是感觉有点难维护&#xff0c;就还是用上面的方法了 …

Labview for循环精讲

本文详细介绍Labview中For循环的使用方法&#xff0c;从所有细节让你透彻的看明白For循环是如何使用的&#xff0c;如果有帮助的话记得点赞加关注~ 1. For循环结构 从最简单的地方讲起&#xff0c;一个常用的for循环结构是由for循环结构框图、循环次数、循环计数(i)三部分组成…