精华内容
下载资源
问答
  • 主要给大家介绍了关于Java8新特性Stream的完全使用指南,文中通过示例代码介绍的非常详细,对大家学习或者使用Java8具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • 一、为什么引入Stream流 流是一系列与特定存储机制无关的元素——实际上,流并没有“存储”之说。使用流,无需迭代集合中的元素,就可以从管道提取和操作元素。这些管道通常被组合在一起,形成一系列对流进行操作的...

    一、为什么引入Stream流

    流是一系列与特定存储机制无关的元素——实际上,流并没有“存储”之说。使用流,无需迭代集合中的元素,就可以从管道提取和操作元素。这些管道通常被组合在一起,形成一系列对流进行操作的管道。

    在大多数情况下,将对象存储在集合中是为了处理他们,因此你将会发现你将编程的主要焦点从集合转移到了流上,流的一个核心的好处是,它使得程序更加短小并且更易理解。当Lambda表达式和方法引用和流一起使用的时候会让人感觉自成一体。
    二、如何使用Stream流

    流操作的类型有三种:创建流,修改流元素(中间操作 Intermediate Operations),消费流元素(终端操作 Terminal Operations)

    创建Stream流
    
    使用Arrays.stream()方法创建
    
    Integer[] arr = new Integer[]{1,2,3,4,5};
    Arrays.stream(arr).filter(num -> num > 3);
    
    使用Stream.of ()方法创建
    
    Integer[] arr = new Integer[]{1,2,3,4,5};
    Stream.of(arr).filter(num -> num > 3);
    
    查看of()的源码中得知,该方法也是调用了Arrays.stream()方法实现的
    
    /**
    * Returns a sequential ordered stream whose elements are the specified values.
    *
    * @param <T> the type of stream elements
    * @param values the elements of the new stream
    * @return the new stream
    */
    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
    	return Arrays.stream(values);
    }
    
    使用Collection.stream()方法创建
    
    List<String> list = new ArrayList<>(1);
    list.stream().forEach(str -> System.out.println(str));
    
    使用Stream.iterate()方法创建
    
    Stream.iterate(1, num -> num + 2).limit(10).forEach(num -> System.out.println(num));
    
    使用Stream.generate()方法创建
    
    Stream.generate(() -> Arrays.asList(arr)).limit(1).forEach(num -> System.out.println(num));
    
    修改流元素(中间操作 Intermediate Operations)
    

    中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。http://www.mitmia.com
    1、跟踪和调试

    peek() 操作的目的是帮助调试,允许你无修改地查看流中的元素

    // streams/Peeking.java
    class Peeking {
    public static void main(String[] args) throws Exception {
    FileToWords.stream(“Cheese.dat”)
    .skip(21)
    .limit(4)
    .map(w -> w + " ")
    .peek(System.out::print)
    .map(String::toUpperCase)
    .peek(System.out::print)
    .map(String::toLowerCase)
    .forEach(System.out::print);
    }
    }

    输出结果:

    Well WELL well it IT it s S s so SO so

    因为 peek() 符合无返回值的 Consumer 函数式接口,所以我们只能观察,无法使用不同的元素来替换流中的对象。
    2、流元素排序

    sorted()方法是需要遍历整个流的,并在产生任何元素之前对它进行排序。因为有可能排序后集合的第一个元素会在未排序集合的最后一位。

    @Test
    public void sortedTest() {
    List numList = Lists.newArrayList();
    numList.add(8);
    numList.add(2);
    numList.add(6);
    numList.add(9);
    numList.add(1);
    List sortList = numList.stream().sorted(Integer::compareTo).collect(Collectors.toList());
    System.out.println(sortList);
    }
    http://www.mmaae.com
    输出结果:

    [1, 2, 6, 8, 9]

    3、移除元素

    distinct() 可用于消除流中的重复元素。相比创建一个 Set 集合,该方法的工作量要少得多。
    
    @Test
    public void distinctTest() {
        Stream.of(6, 8, 9, 6, 2, 8).distinct().forEach(i -> System.out.print(i + ", "));
    }
    
    输出结果:
    
    6, 8, 9, 2, 
    
    filter(Predicate):若元素传递给过滤函数产生的结果为true ,则过滤操作保留这些元素。
    
    @Test
    public void filterTest() {
    	Stream.of(6, 9, 2, 8).filter(num -> num > 5).sorted().forEach(i -> System.out.print(i + ", "));
    }
    
    输出结果:
    
    6, 8, 9, 
    

    4、映射,应用函数到元素

    map(Function):将函数操作应用在输入流的元素中,对一个流中的值进行某种形式的转换,并将返回值传递到输出流中
    
    @Test
    public void mapTest() {
    	Stream.of("abc", "qw", "mnkh").map(String::length).forEach(n -> System.out.format("%d ", n));
    }
    
    输出结果:
    
    3 2 4 
    
    mapToInt(ToIntFunction):操作同上,但结果是 IntStream
    
    Stream.of("5", "7", "9").mapToInt(Integer::parseInt).forEach(n -> System.out.format("%d ", n));
    
    mapToLong(ToLongFunction):操作同上,但结果是 LongStream
    
    Stream.of("17", "19", "23").mapToLong(Long::parseLong).forEach(n -> System.out.format("%d ", n));
    
    mapToDouble(ToDoubleFunction):操作同上,但结果是 DoubleStream
    
    Stream.of("17", "1.9", ".23").mapToDouble(Double::parseDouble).forEach(n -> System.out.format("%f ", n));
    
    flatMap() 做了两件事:将产生流的函数应用在每个元素上(与 map() 所做的相同),然后将每个流都扁平化为元素,因而最终产生的仅仅是元素。
    
    List<Integer> listA = Lists.newArrayList();
    listA.add(1);
    listA.add(6);
    List<Integer> listB = Lists.newArrayList();
    listB.add(10);
    listB.add(2);
    Map<String, List<Integer>> abMap = Maps.newHashMap();
    abMap.put("A", listA);
    abMap.put("B", listB);
    // 需获取A和B集合中大于5的元素
    abMap.values().stream().flatMap(num -> num.stream().filter(n -> n > 5)).collect(Collectors.toList())
        .forEach(System.out::println);
    
    输出结果:
    
    6
    10
    
    flatMapToInt(Function):当 Function 产生 IntStream 时使用。
    
    flatMapToLong(Function):当 Function 产生 LongStream 时使用。
    
    flatMapToDouble(Function):当 Function 产生 DoubleStream 时使用。
    

    5、集合流切片,可实现分页

    limit(n)方法会返回一个包含n个元素的新的流(若总长小于n则返回原始流)。
    
    skip(n)方法正好相反,它会丢弃掉前面的n个元素。
    
    // 查询第二页的数据
    Integer pageNumber = 2;
    Integer pageSize = 10;
    Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).skip((pageNumber - 1) * pageSize).limit(pageSize)
    				.forEach(System.out::println);
    
    输出结果:
    
    11
    12
    
    消费流元素(终端操作 Terminal Operations)
    

    终端操作总是我们在流管道中所做的最后一件事,该操作将会获取流的最终结果。
    1、数组结果输出

    toArray():将流转换成适当类型的数组。
    toArray(generator):在特殊情况下,生成自定义类型的数组。
    

    2、循环结果输出

    forEach(Consumer)常见如 System.out::println 作为 Consumer 函数。
    
    forEachOrdered(Consumer): 保证 forEach 在并行流处理时按照原始流顺序操作。
    
    Arrays.stream(new Random(45).ints(0, 1000).limit(100).toArray()).limit(10).parallel().forEachOrdered(n -> 						System.out.format("%d ", n));
    

    3、collect收集结果

    collect(Collector):使用 Collector 收集流元素到结果集合中。
    
    collect(Supplier, BiConsumer, BiConsumer):同上,第一个参数 Supplier 创建了一个新的结果集合,第二个参数 BiConsumer 将下一个元素收集到结果集合中,第三个参数 BiConsumer 用于将两个结果集合合并起来。
    
    Collectorts类为我们提供了常用的收集类的各个工厂方法:
    
    将一个流收集到一个List中可以这样用
    
    Lists.newArrayList().stream().collect(Collectors.toList());
    
    收集到Set中可以这样用
    
    Lists.newArrayList().stream().collect(Collectors.toSet());
    
    收集到Map中可以这样用
    
    Lists.newArrayList(new User("Johnson", "重庆")).stream().collect(Collectors.toMap(User::getName, User::getAddress));
    
    收集到Set时,控制Set的类型可以这样用
    
    Lists.newArrayList().stream().collect(Collectors.toCollection(TreeSet::new));
    
    将字流中的字符串连接并收集起来
    
    Lists.newArrayList().stream().collect(Collectors.joining(","));
    
    各种聚合操作
    
    // 获取流中的总和,平均值,最大值,最小值,一次性收集流中的结果
    List<Integer> listA = Lists.newArrayList(1, 2, 3, 4, 5);
    listA.stream().collect(Collectors.summingInt(Integer::intValue));
    listA.stream().collect(Collectors.averagingInt(Integer::intValue));
    listA.stream().collect(Collectors.maxBy(Integer::compareTo));
    listA.stream().collect(Collectors.minBy(Integer::compareTo));
    listA.stream().collect(Collectors.summarizingInt(Integer::intValue));
    // 分组分片,返回结果:{"重庆渝北":[{"address":"重庆渝北","name":"Johnson"},{"address":"重庆渝北","name":"Jack"}],"重庆江北":[{"address":"重庆江北","name":"Tom"}]}
    List<User> listB = Lists.newArrayList(new User("Johnson", "重庆渝北"), new User("Tom", "重庆江北"), new User("Jack", "重庆渝北"));
    System.out.println(JSON.toJSONString(listB.stream().collect(Collectors.groupingBy(User::getAddress))));
    

    4、组合流中元素

    reduce(BinaryOperator):使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional
    
    // 结果为15
    System.out.println(Stream.of(1, 2, 3, 4, 5).reduce((x, y) -> x + y).get());
    
    reduce(identity, BinaryOperator):功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果
    
    // 设置初始值为10则结果为25
    System.out.println(Stream.of(1, 2, 3, 4, 5).reduce(10, (x, y) -> x + y));
    // 集合流为空,则结果默认为初始值a
    List<String> list = Lists.newArrayList();
    System.out.println(list.stream().reduce("a", (x, y) -> x.length() > 1 ? x : y));
    
    reduce(identity, BiFunction, BinaryOperator):在串行流(stream)中,该方法跟第二个方法一样,即第三个参数不会起作用。在并行流中,我们知道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,BiFunction)一样,而第三个参数BinaryOperator函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(BinaryOperator)流程进行规约
    
    // 第三个参数在并行流中起效,将每个线程的执行结果当成一个新的流
    List<Integer> listA = Lists.newArrayList(1, 2, 3, 4, 5);
    // 串行流运行结果:15
    System.out.println(listA.stream().reduce(0, (x, y) -> x + y, (i, j) -> i * j));
    // 并行流运行结果:120
    System.out.println(listA.parallelStream().reduce(0, (x, y) -> x + y, (i, j) -> i * j));
    

    5、流中元素匹配

    allMatch(Predicate) :如果流的每个元素提供给 Predicate 都返回 true ,结果返回为 true。在第一个 false 时,则停止执行计算。
    
    // 数组中第一个元素小于2,则停止匹配返回结果:flase
    System.out.println(Stream.of(1, 2, 3, 4, 5).allMatch(n -> n > 2));
    // 数组中所有元素都大于0,则停止匹配返回结果:true
    System.out.println(Stream.of(1, 2, 3, 4, 5).allMatch(n -> n > 0));
    
    anyMatch(Predicate):如果流的任意一个元素提供给 Predicate 返回 true ,结果返回为 true。在第一个 true 是停止执行计算。
    
    // 数组中第三个元素大于2,则停止匹配返回结果:true
    System.out.println(Stream.of(1, 2, 3, 4, 5).anyMatch(n -> n > 2));
    
    noneMatch(Predicate):如果流的每个元素提供给 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。
    
    // 数组中第三个元素大于2,则停止匹配返回结果:true
    System.out.println(Stream.of(1, 2, 3, 4, 5).noneMatch(n -> n > 2));
    

    6、流中元素查找

    findFirst():返回第一个流元素的 Optional,如果流为空返回 Optional.empty。
    
    // 根据条件过滤后取第一个元素
    System.out.println(Stream.of(1, 2, 3, 4, 5).filter(n -> n > 2).findFirst().get());
    
    findAny():返回含有任意流元素的 Optional,如果流为空返回 Optional.empty。
    
    // 根据条件过滤后找到任何一个所匹配的元素,就返回,此方法在对流并行执行时效率高
    System.out.println(Stream.of(1, 2, 3, 4, 5).parallel().filter(n -> n > 2).findAny().get());
    

    7、收集流信息

    count():流中的元素个数。
    
    max(Comparator):根据所传入的 Comparator 所决定的“最大”元素。
    
    min(Comparator):根据所传入的 Comparator 所决定的“最小”元素。
    
    average() :求取流元素平均值。
    
    sum():对所有流元素进行求和。
    
    summaryStatistics():生成有关此流元素的各种摘要数据。
    
    // 获取流中元素数量,返回结果:5
    System.out.println(Stream.of(1,2,3,4,5).count());
    // 获取流中最大值,返回结果:5
    System.out.println(Stream.of(1,2,3,4,5).max(Integer::compareTo).get());
    // 获取流中最小值,返回结果:1
    System.out.println(Stream.of(1,2,3,4,5).min(Integer::compareTo).get());
    // 获取流中元素平均值,返回结果:3.0
    System.out.println(Stream.of(1,2,3,4,5).mapToInt(Integer::intValue).average().getAsDouble());
    // 获取流中各种摘要数据,返回结果:IntSummaryStatistics{count=5, sum=15, min=1, average=3.000000, max=5}
    System.out.println(Stream.of(1,2,3,4,5).mapToInt(Integer::intValue).summaryStatistics());
    // 获取流中元素总和,返回结果:15
    System.out.println(Stream.of(1,2,3,4,5).mapToInt(Integer::intValue).sum());
    

    三、总结

    流式操作改变并极大地提升了 Java 语言的可编程性,并可能极大地阻止了 Java 编程人员向诸如 Scala 这种函数式语言的流转。

    展开全文
  • Java8新特性[Stream API]前言区别什么是StreamStream操作的三部曲创建流中间操作终止操作Stream流的操作筛选与切片映射排序终止操作规约收集总结 前言 了解Stream Java8中有两个比较大的改变 Lambda表达式 Stream ...

    前言

    了解Stream
    Java8中有两个比较大的改变

    • Lambda表达式
    • Stream API (java.util.stream.*)

    Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询,也可以使用Stream API来并行操作,简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

    区别

    这里的Stream流和IO流是有区别的,这里是我们在数据源流向 目标源的时候,会产生一系列流水线式的中间操作,最后产生一个新流,同时原来的数据源是不会改变的。

    这里的中间操作可能是:切片,排序,筛选等
    在这里插入图片描述

    什么是Stream

    Stream是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,“集合讲的是数据,流讲的是计算”!

    注意:

    • Stream 自己不会存储元素
    • Stream 不会改变源对象,相反,他们会返回一个持有结果的新Stream
    • Stream 操作是延迟的,这就意味着他们会等到需要结果的时候才执行的

    Stream操作的三部曲

    创建流

    一个数据源(如:集合,数组),获取一个流

    • 通过Collection系列集合的 stream() 或者 parallelStream() 获取流
    List<String> list = new ArrayList<>();
    Stream<String> stream =  list.stream();
    
    • 通过Arrays 中的静态方法,获取数组流
    Employee[] employees = new Employee[10];
    Stream<Employee> stream1 = Arrays.stream(employees);
    
    • 通过Stream中的静态方法of(),获取流
    Stream<String> stream3 = Stream.of("aa", "bb", "cc");
    
    • 创建无限流
    Stream<Integer> stream4 = Stream.iterate(0, (x) -> x +2 );
    

    中间操作

    一个中间操作链,对数据源的数据进行处理

    终止操作

    一个终止操作,执行中间操作链,并产生结果
    在这里插入图片描述

    Stream流的操作

    多中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否者中间操作不会执行任何的处理,而在终止操作时一次性全部处理,这样被称为 惰性求值

    筛选与切片

    • filter( Predicate p):接收Lambda,从流中排除某些元素
    • distinct():筛选,通过流所生成的hashCode()和equals()去除重复元素
    • limit(long maxSize):截断流,使其元素不超过给定数量
    • skip(long n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 18, 3333),
                    new Employee("李四", 38, 55555),
                    new Employee("王五", 50, 6666.66),
                    new Employee("赵六", 16, 77777.77),
                    new Employee("田七", 8, 8888.88)
            );
            Stream<Employee> stream = employees.stream();
            stream.filter((x) -> x.getAge() > 30)
                    .limit(2)
                    .forEach(System.out::println);
    

    映射

    map接收Lambda,将元素转换成其它形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新元素。flatMap 接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流。

        public static void test2() {
            List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
            list.stream().map((x) -> x.toUpperCase()).forEach(System.out::println);
        }
    

    排序

    • sorted():自然排序
    • sorted(Comparator com):定制排序
        public static void test3() {
            List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
            // 自然排序,按照字典进行排序
            list.stream().sorted().forEach(System.out::println);
    
            // 定制排序
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 18, 3333),
                    new Employee("李四", 38, 55555),
                    new Employee("王五", 50, 6666.66),
                    new Employee("赵六", 16, 77777.77),
                    new Employee("田七", 8, 8888.88)
            );
            employees.stream().sorted((e1, e2) -> {
                if(e1.getAge() == e2.getAge()) {
                    return e1.getName().compareTo(e2.getName());
                } else {
                    return Integer.compare(e1.getAge(), e2.getAge());
                }
            }).forEach(System.out::println);
        }
    

    输出结果:

    aaa
    bbb
    ccc
    ddd
    Employee{name='田七', age=8, salary=8888.88}
    Employee{name='赵六', age=16, salary=77777.77}
    Employee{name='张三', age=18, salary=3333.0}
    Employee{name='李四', age=38, salary=55555.0}
    Employee{name='王五', age=50, salary=6666.66}
    

    终止操作

    执行下列操作后,Stream流就会进行终止执行

    查找与匹配

    • allMatch:检查是否匹配所有元素
    • anyMatch:检查是否至少匹配一个元素
    • noneMatch:检查是否一个都没匹配
    • findFirst:返回第一个元素
    • findAny:返回当前流中任意一个元素
    • count:返回流中元素的个数
    • max:返回当前流中最大值
    • min:返回当前流中最小值
    • forEach:内部迭代
        public static void test4() {
            // 定制排序
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 18, 3333),
                    new Employee("李四", 38, 55555),
                    new Employee("王五", 50, 6666.66),
                    new Employee("赵六", 16, 77777.77),
                    new Employee("田七", 8, 8888.88)
            );
            Boolean isAllMatch = employees.stream().allMatch((x) -> x.getAge() > 10);
            System.out.println("是否匹配所有元素:" + isAllMatch);
    
            Boolean isAnyMatch = employees.stream().anyMatch((x) -> x.getAge() > 10);
            System.out.println("是否匹配至少一个元素:" + isAnyMatch);
        }
    

    规约

    格式:reduce(T identity, BinaryOperator) / reduce(BinaryOperator)

    可以将流中元素反复结合,得到一个新值

    这个reduce,其实有点类似于Hadoop中的mapReduce,先做map操作,然后做reduce操作

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5 ,6 ,7 ,8 , 9, 10);
    // 按照下面的规则进行累加操作
    // reduce的规约,就是把前面定义的起始值,作为了x
    Integer num = list.stream().reduce(0, (x, y) -> x + y);
    System.out.println(num);
    

    收集

    Collection将流转换成其它形式,接收一个Collector接口实现,用于给Stream中元素做汇总的方法

    格式:collect(Collector c)

    Collector接口实现方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常用收集器实例

        /**
         * 收集器
         */
        public static void test6() {
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 18, 3333),
                    new Employee("李四", 38, 55555),
                    new Employee("王五", 50, 6666.66),
                    new Employee("赵六", 16, 77777.77),
                    new Employee("田七", 8, 8888.88)
            );
            // 收集放入list中
            List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
            list.forEach(System.out::println);
        }
    

    总结

    老规矩,一键三连,点赞、关注+收藏。谢谢各位同学了

    展开全文
  • JAVA1.8新特性Stream

    千次阅读 2018-12-05 23:37:34
    stream流是Java8新特性,它也是有关于集合的新api; Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Java 8 中的 Stream 是对集合(Collection)对象功能的...

    今天我们来学习一下Java 8 的新特新—>Stream流;

    Stream流

    stream流是Java8的新特性,它也是有关于集合的新api;
    Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作;
    Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性;
    下面我们用一个例子来引入Stream流的操作:

    Stream流的数据操作特性

    这里有一个集合

    List<Integer> list = Arrays.asList(1,2,3,4,5);
    

    我们要写一个方法找偶数,返回一个新的list包含结果;
    用我们以前的方法做的话就会比较繁琐:

    public static void main(String[] args) {
            List<Integer> list = Arrays.asList(1,2,3,4,5);
            System.out.println(exec(list, new Predicate<Integer>(){
                @Override
                public boolean test(Integer i) {
                    return i % 2 == 0;
                }
            }))//System.out.println(exec(list, i ->  i % 2 == 0));
        }
        // 设计模式中,策略模式
        public static List<Integer> exec(List<Integer> list, Predicate<Integer> predicate) {
            List<Integer> list2 = new ArrayList<>();
            for(Integer i :list) {
                if(predicate.test(i)) {
                    list2.add(i);
                }
            }
            return list2;
        }
    

    在这里我们使用了设计模式中的策略模式,将它的处理方法拿出来,可以对它的使用方法进行主动的编写,同时我们用了一个新的模式Predicate,这个模式叫做断言模式,顾名思义就是对我们要进行的处理进行断言操作,断定它能进行的功能;比如上面的例子就是我们求出了list集合中所有的偶数,那我们如果不想对他进行求偶操作呢?比如我们想求出集合中所有大于三的数据呢?
    这个时候我们就可以改变这个断言,重新写入想要的操作;

     System.out.println(exec(list, new Predicate<Integer>(){
                @Override
                public boolean test(Integer i) {
                    return i >3;
                }
    

    在不用断言模式的时候,我们甚至需要新写一个方法,然后调用它,有了断言模式,我们就可以主动的给exec这个方法传入我们想要的策略;
    甚至我们可以对断言进行简写;即lambda表达式;
    在这里插入图片描述
    这里我们可以看到这个接口是一个函数式接口,就是单方法接口;所以我们完全可以用lambda表达式给他写入断言;

    System.out.println(exec(list,i -> i % 2 == 0);
    System.out.println(exec(list,i -> i > 3);
    

    用了lambda表达式就大大减少了我们的代码量;
    可是这也是比较麻烦的 ;而且在之前我们遍历集合的时候就需要循环,或者 Iterator 迭代器来遍历;这也都是很浪费时间和空间的;
    那有没有一种方法,是我们不用调用方法,直接用语句来获得所有的偶数呢?这个时候我们就需要用到jdk1.8的新特性,Stream流,将数组元素变为一条数据流,然后对这个数据流进行操作,过滤或者收集想要的数据;

    • 特点:
    1. Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

    2. Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

    3. Stream 的数据源本身可以是无限的。

    Stream()类的方法

    1. filter (过滤器)
      这个过滤器的方法就是对流中的数据一个一个的进行筛选,看看它是不是符合规则,如果不符合就拦截住,也就是舍弃,如果符合要求,则让他通过,进行下一步的操作,说白了就像一个滤网一样,我们可以设置过滤的规则,然后它对数据流进行筛选;
      比如上面的例子来说我们就可以用这样的方法;
    List<Integer> list = Arrays.asList(1,2,3,4,5,6);
    List<Integer> stream = list.stream().filter( i -> 
    		i%2  == 0).collect(Collectors.toList());
    System.out.print(stream);
    

    在这里插入图片描述
    我们可以看到filter的参数就是一个Predicae<>断言;也就是说我们可以给它里面传一个lambda表达式,就是过滤的规则; 同时它返回的又是一个Stream流对象,这时我们又可以用它的collect(收集器),对过滤下来的数据收集起来,把他存入一个List集合;然后这时候我们输出得到的这个集合,就是我们收集的数据;

    1. map (映射)
      映射的, lambda把原有的元素转换为另一个元素, 不会改变个数;
      上面的filter过滤器就是给把我们的数据按我们的规则筛选出来,那map()方法就是将我们的元素按照规则映射成另外的元素;比如我们要得到上面的list集合中的所有元素的2倍的集合,这时候我们就需要用到map()方法;
    	List<Integer> list4 = Arrays.asList(1, 2, 3, 4, 5, 6);
            List<Integer> collect = list4.stream().map(i -> 
            i * 2).collect(Collectors.toList());
            System.out.println(collect);
    

    在这里插入图片描述
    这只是简单的映射,我们又得到了需要的数据,同时原集合仍然存在;我们再看看map()方法的参数;
    在这里插入图片描述

    在这里插入图片描述

    这里我们需要一个Function()参数,这个模式的方法lambda表达式的写法就是一个参数,返回一个结果;

    	map(i ->  i * 2)
    

    这句映射就很直观的帮我们解释了Function接口的特性;

    1. == flatMap(扁平化映射)==
      这个类就好比是流的类型转换;
      比如我们现在定义了一个list集合,它的元素是字符串数组;
    List<String[]> list = new ArrayList<>();
            list.add(new String[]{"张三", "李四"});
            list.add(new String[]{"王五", "张三"});
            list.add(new String[]{"钱七", "周八"});
    

    如果我们想将它里面的每个字符串提取出来,组成一个新的集合,按照原来的方法我们就需要两重遍历,先遍历集合,后遍历数组,得到每一个字符串元素,再新建一个集合,将它们存进去;

     List<String> list2 = new ArrayList<>();
    
            for (String[] strings : list) {
                for (String string : strings) {
                    list2.add(string);
                }
            }
            System.out.println(list2);
    

    可如果用flatmap方法做扁平化映射时就特别简单,我们一句话就可以得到这个集合;

    List<String> list3 = list.stream().flatMap( s -> 
    Arrays.stream(s)).collect(Collectors.toList());
    

    flatmap的参数同样是一个Function,只是这次我们传入的参数是list集合的元素String[]数组,把它映射成一个数组流Arrays.stream,然后collect收集起来;这个方法就很方便;
    它就可以用在我们平时大规模数据转换的时候,用流的方式,得到需要的格式;

    1. forEach
      遍历流,接收一个Consumer
    list3.stream().forEach( (a) -> {
        System.out.println(a);
    } );
    
    1. map的流遍历
      接收一个BiConsumer
    Map<String, String> map = new HashMap<>();
    map.put("a", "张");
    map.put("b", "李");
    map.forEach( (key, value) -> {
        System.out.println("key:" +key + " value:" + value);
    } );
    

    重要模式

    上面说了很多种的模式,再这里我们统一进行一下说明;

    1. Predicate 断言接口
      对应的lambda 一个参数,返回结果是boolean
      (a) -> { return true|false; }

    2. BiPredicate 双参数断言
      对应的lambda 两个参数,返回结果是boolean
      (a, b) -> { return true|false; }

    3. Function 函数接口
      对应的lambda 一个参数,一个返回结果,参数和返回结果的类型可以不一样

    4. BiFunction 双参数函数接口
      两个参数,一个结果
      (a, b) -> { 根据ab返回一个结果}

    5. Consumer 消费接口
      一个参数 没有结果
      (a) -> { 不需要return }

    6. BiConsumer 双参数消费接口
      两个参数,没有结果
      (a,b) -> { 不需要return }

    7. Supplier 生产者接口
      没有参数,返回一个结果
      () -> {return 结果}
      有了这些模式知识的帮忙我们就可以更好的理解Stream()中方法的参数需求了;

    7. 其它常见api

    求个数count()

    System.out.println(list3.stream().count());
    

    去除重复distinct()

    System.out.println(list3.stream().distinct().collect(toList()));
    

    获取最大最小值(参数需要一个比较器)

    // 返回的是Optional 类型,怕集合为空时,没有合法的最大值
    List<String> list4 = Arrays.asList("zhang", "li", "zhao", "wang"); 
    System.out.println(list4.stream().max((a, b) -> a.compareTo(b)));
    System.out.println(list4.stream().min((a, b) -> a.compareTo(b)));
    

    如果是数字流,除了最大最小值外,还有平均值,和

    System.out.println(IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).max());
    System.out.println(IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).min());
    System.out.println(IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).average());
    System.out.println(IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).sum());
    

    有了上面的知识我们来做一个比较复杂的需求:
    我们定义了一个学生类,它的属性包括名字,性别,所在城市;

    public class Student {
    
        private String name;
        private String sex;
        private String city;
    
        public Student(String name, String sex, String city) {
            this.name = name;
            this.sex = sex;
            this.city = city;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    }
    

    然后我们定义一个集合存放学生类的对象

     List<Student> students = Arrays.asList(
                    new Student("zhang", "男", "西安"),
                    new Student("li", "男", "西安"),
                    new Student("wang", "女", "北京"),
                    new Student("zhao", "女", "上海"),
                    new Student("zhou", "男", "北京")
            );
    

    这时我们如果想要按照性别进行分组存储呢?(男生存在一起,女生存在一起)
    按照我们本来的方法需要定义map数组,按照键值对形式,将性别和对象形成映射,然后存储起来;
    具体代码:

    Map<String, List<Student>> map2 = new HashMap<>();
            for (Student student : students) {
                if (student.getSex().equals("男")) {
                    List<Student> nan = map2.get("男");
                    if (nan == null) {
                        nan = new ArrayList<>();
                        map2.put("男", nan);
                    }
                    nan.add(student);
                } else {
                    List<Student> nv = map2.get("女");
                    if (nv == null) {
                        nv = new ArrayList<>();
                        map2.put("女", nv);
                    }
                    nv.add(student);
                }
            }
            System.out.println(map2);
    
    

    我们发现上面的逻辑很复杂,我们也很容易出错,同时这只是我们按照性别分组,那如果类别特别多的话,比如我们按照所在城市分组呢?难道我们每一个都要进行判断吗?显然这是不现实的,这时我们就必须用Stream流来实现了:

    //按照性别分组
    Map<String, List<Student>> map3 = students.stream().collect(
    Collectors.groupingBy( s -> s.getSex() ));
            System.out.println(map3);
    

    这里我们用了groupingBy方法,传入的参数是一个Function,分组的标准就是我们function的返回属性;

    //按照所在城市分组
    Map<String, List<Student>> map4 = students.stream().collect(
    Collectors.groupingBy( s -> s.getCity()));
            System.out.println(map4);
    

    答案:
    {
    女=[Student{name=‘wang’, sex=‘女’, city=‘北京’}, Student{name=‘zhao’, sex=‘女’, city=‘上海’}],
    男=[Student{name=‘zhang’, sex=‘男’, city=‘西安’}, Student{name=‘li’, sex=‘男’, city=‘西安’}, Student{name=‘zhou’, sex=‘男’, city=‘北京’}]
    }

    {
    上海=[Student{name=‘zhao’, sex=‘女’, city=‘上海’}],
    西安=[Student{name=‘zhang’, sex=‘男’, city=‘西安’}, Student{name=‘li’, sex=‘男’, city=‘西安’}],
    北京=[Student{name=‘wang’, sex=‘女’, city=‘北京’}, Student{name=‘zhou’, sex=‘男’, city=‘北京’}]
    }


    展开全文
  • StreamJava8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以...

    写在前面

    Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*) ,那什么是Stream API呢?Java8中的Stream又该如何使用呢?

    什么是Stream?

    Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。

    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式

    流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算! ”

    注意:
    ① Stream 自己不会存储元素。
    ② Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    ③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

    Stream操作的三个步骤

    • 创建 Stream

    一个数据源(如: 集合、数组), 获取一个流。

    • 中间操作

    一个中间操作链,对数据源的数据进行处理。

    • 终止操作(终端操作)

    一个终止操作,执行中间操作链,并产生结果 。

    在这里插入图片描述

    如何创建Stream?

    Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

    1.获取Stream

    • default Stream stream() : 返回一个顺序流

    • default Stream parallelStream() : 返回一个并行流

    2.由数组创建Stream

    Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

    • static Stream stream(T[] array): 返回一个流

    重载形式,能够处理对应基本类型的数组:

    • public static IntStream stream(int[] array)

    • public static LongStream stream(long[] array)

    • public static DoubleStream stream(double[] array)

    3.由值创建流

    可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。

    • public static Stream of(T… values) : 返回一个流

    4.由函数创建流

    由函数创建流可以创建无限流。

    可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流 。

    • 迭代

    public static Stream iterate(final T seed, final UnaryOperator f)

    • 生成

    public static Stream generate(Supplier s)

    Stream的中间操作

    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”

    1.筛选与切片

    在这里插入图片描述

    2.映射

    在这里插入图片描述

    3.排序

    在这里插入图片描述

    Stream 的终止操作

    终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer,甚至是 void 。

    1.查找与匹配

    在这里插入图片描述

    在这里插入图片描述

    2.规约

    在这里插入图片描述

    3.收集

    在这里插入图片描述

    Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、 Set、 Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例, 具体方法与实例如下表

    在这里插入图片描述

    在这里插入图片描述

    并行流与串行流

    并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

    Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与
    sequential() 在并行流与顺序流之间进行切换

    Fork/Join 框架

    1.简单概述

    Fork/Join 框架: 就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总.

    在这里插入图片描述

    2.Fork/Join 框架与传统线程池的区别

    采用 “工作窃取”模式(work-stealing):
    当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

    相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能。

    写在最后

    如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Java8新特性。

    最后,附上Java8新特性核心知识图,祝大家在学习Java8新特性时少走弯路。

    在这里插入图片描述

    展开全文
  • 转载:https://mp.weixin.qq.com/s/d_UumCMUrXtjXKGDhg-1DQ写在前面Java8中有两大最为重要的改变。...Java8中的Stream又该如何使用呢?什么是Stream?Java8中有两大最为重要的改变。第一个是 Lam...
  • 写在前面Java8中有两大最为重要的改变。...Java8中的Stream又该如何使用呢?什么是Stream?Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。Strea...
  • 转载:https://mp.weixin.qq.com/s/d_UumCMUrXtjXKGDhg-1DQ写在前面Java8中有两大最为重要的改变。...Java8中的Stream又该如何使用呢?什么是Stream?Java8中有两大最为重要的改变。第一个是 Lam...
  • 写在前面Java8中有两大最为重要的改变。...Java8中的Stream又该如何使用呢?什么是Stream?Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。Strea...
  • (可以参见:《【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?》)Java8中的Stream API有哪些中间操作?(可以参见:《【Java8新特性Stream API有哪些中间操作?看完你也可以吊打面试官!!》)...
  • Stream字面意思是流,在java中是指一个来自数据源的元素队列并支持聚合操作,存在于java.util包中,又或者说是能应用在一组元素上一次执行的操作序列。(stream是一个由特定类型对象组成的一个支持聚合操作的队列。)...
  • 直接看测试代码,关于什么是stream之类的介绍就不多说了。 首先创建了一个简单的User类 package com.java8.pojo; import lombok.Data; import java.util.Objects; /** * &lt;p&gt;&lt;/p&gt; ...
  • 并且上一章也讲了一些关于Stream的常用操作,比如:map()、filter()、concat()、reduce()、max()、min()、distinct()等常用操作。这篇我就分享一下Stream中List、Set、Map之间的转换操作: 准备工作:新建一个...
  • 最近在学习java8新特性,在看到lambda时用到了函数式接口。 发现函数式接口的定义是一个接口只有一个抽象方法,但我在查看Stream时发现其中有许多抽象方法,不知到是我对函数式接口的定义理解有问题还是Stream...
  • 首先先拿github上的两篇文章作为关于Stream API的专业性技术指导 http://www.cnblogs.com/CarpenterLee/p/6545321.html Java Stream API入门篇 http://www.cnblogs.com/CarpenterLee/p/6637118.html Java Stream...
  • Java8新特性——并行流parallelStream

    万次阅读 2014-06-07 21:45:48
    (本博客的代码根据 java8新特性教程 学习整理,加上个人的理解而成,关于某个新特性的介绍代码里的注释已经阐述清楚,故不再写文字介绍,直接看代码吧!)  本篇介绍java8的新特性之一:lambda表达式。
  • Java8新特性

    2021-04-06 06:17:05
    Java8新特性 Java8介绍 关于Java8 Java 8(又称为jdk 1.8)是Java语言开发的一个主要版本。 Java 8是oracle公司于2014年3月发布,可以看成是自Java5以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具...
  • 初学java8的语法,对于单独使用lambda表达式,1.8的静态方法引用表示法以及1.8的streamapi中forEach()的引用已经有了一个初步了解,但是在做练习的过程中,遇到了如下代码:public class Java8 {private static ...
  • 本文主要讲解关于Stream中reduce的使用方式以及Collect使用方式,同时展示如何自定义收集器。 提示:如果大家对lambda表达式中的四大基础函数不清楚,推荐大家优先看下四大内置核心函数式接口以及看下关于reduce...
  • Java8新特性[HashMap优化]前言其他主要新特性HashMap优化HashMap1.7HashMap1.7存在死链问题HashMap每次扩容为什么是2倍JDK1.8结构变化ConcurrentHashMap变化为何JDK8要放弃分段锁?内存结构优化总结 前言 本文开始...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 125
精华内容 50
关键字:

关于java8新特性stream

java 订阅