• 流,用声明性的方式处理数据集


    引入流

    Stream API的代码

    • 声明性 更简洁,更易读
    • 可复合 更灵活
    • 可并行 性能更好

    流是什么?

    • 它允许以声明方式处理数据集合
    • 遍历数据集的高级迭代器
    • 透明地并行处理
    • 简短定义:从支持数据处理操作的源生成的元素序列
    • 特点:流水线和内部迭代

    返回低热量菜肴的名称

    List<Dish> menu = FakeDb.getMenu();
    
    List<String> lowCaloricDishesName =
        // menu.stream()
        menu.parallelStream() // 并行处理
        .filter(d -> d.getCalories() < 400) // 筛选低卡路里
        .sorted(comparing(Dish::getCalories)) // 按卡路里排序
        .map(Dish::getName) // 提取菜肴名称
        .collect(toList()); // 返回list
    
    System.out.println(lowCaloricDishesName);
    

    流与集合?

    • 流只能遍历一次
    • 流是内部迭代,集合是外部迭代

    流的操作?

    • 流的使用:一个数据源,一个中间操作链,一个终端操作

    • 流的操作有两类:中间操作与终端操作

    使用流

    筛选、切片

    • 谓词筛选 filter
    • 去重 distinct
    • 截短流 limit
    • 跳过元素 skip
    // 筛选前2个素菜
    FakeDb.getMenu().stream()
        .filter(d -> d.isVegetarian()) // 这就是谓词筛选
        .limit(2) // 截断流,返回前n个元素
        .forEach(System.out::println);
    
    Arrays.asList(1, 2, 2, 3, 3, 3).stream()
        .distinct() // 去重
        .skip(1) // 跳过元素
        .forEach(System.out::println);
    

    映射

    • 对每个元素应用函数 map
    • 扁平化 flatMap 把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流
    // 提取菜名
    FakeDb.getMenu().stream()
        .map(Dish::getName)
        .forEach(System.out::println);
    
    // 求每个字符串长度
    Arrays.asList("Java 8", "Lambdas", "In", "Action").stream()
        .map(String::length)
        .forEach(System.out::println);
    

    流的扁平化 flatMap

    单词列表返回字符列表

    // 单词列表返回字符列表 如输入列表["Hello", "World"],输出列表["H", "e", "l", "o", "W", "r", "d"]
    // 尝试一:不成功
    Arrays.asList("Hello", "World").stream()
        .map(w -> w.split("")) // 将每个单词转为字符数组  Stream<String[]>
        .distinct()
        .forEach(System.out::println);
    

    Arrays.stream()可以将数组转换成流

    // 尝试二:不成功
    Arrays.asList("Hello", "World").stream()
        .map(w -> w.split("")) // 将每个单词转为字符数组  Stream<String[]>
        .map(Arrays::stream) // 将每个数组转换成一个单独的流 Stream<Stream<String>>
        .distinct()
        .forEach(System.out::println);
    

    使用flatMap

    Arrays.asList("Hello", "World").stream()
        .map(w -> w.split("")) // 将每个单词转为字符数组
        .flatMap(Arrays::stream)// 将各个生成流扁平化为单个流 Stream<String>
        // 各个数组并不是分别映射成一个流,而是映射成流的内容
        .distinct()
        .forEach(System.out::println);
    

    flatMap方法让你先把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流

    flatMap使用举例

    // 数字列表的平方列表
    Arrays.asList(1, 2, 3, 4, 5, 6).stream()
        .map(n -> n * n)
        .forEach(System.out::println);
    System.out.println("----------------------");
    // 两个数组列表的数对
    List<Integer> numbers1 = Arrays.asList(1, 2, 3);
    List<Integer> numbers2 = Arrays.asList(3, 4);
    numbers1.stream()
        .flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
        .forEach(a -> System.out.println(a[0] + "	" + a[1]));
    System.out.println("----------------------");
    // 总和能被3整除的数对
    numbers1.stream()
        .flatMap(i -> numbers2.stream()
                 .filter(j -> (i + j) % 3 == 0)
                 .map(j -> new int[]{i, j}))
        .forEach(a -> System.out.println(a[0] + "	" + a[1]));
    

    查找和匹配

    从单词列表中返回所用到的字符

    allMatch/anyMatch/noneMatch/findFirst/findAny

    匹配

    // 至少匹配一个元素
    boolean match = FakeDb.getMenu().stream()
        .anyMatch(Dish::isVegetarian);
    System.out.println(match);
    
    // 匹配所有元素
    match = FakeDb.getMenu().stream()
        .allMatch(d -> d.getCalories() > 1000);
    System.out.println(match);
    
    // 所有都不匹配
    match = FakeDb.getMenu().stream()
        .noneMatch(d -> d.getCalories() > 1000);
    System.out.println(match);
    

    查找

    Optional<Dish> dish = FakeDb.getMenu().stream()
        .filter(Dish::isVegetarian)
        .findAny();
    
    // 关于返回值 Optional<T> 的用法:
    System.out.println(dish.isPresent()); // 存在返回true,否则返回false
    dish.ifPresent(System.out::println); // 存在就执行代码块
    System.out.println(dish.get()); // 存在返回值,不存在抛异常
    Dish defaultDish = new Dish("pork", false, 800, Dish.Type.MEAT);
    System.out.println(dish.orElse(defaultDish)); // 存在返回值,否则返回默认值
    
    // 查找第一个元素
    Optional<Dish> dish2 = FakeDb.getMenu().stream()
        .filter(Dish::isVegetarian)
        .findFirst();
    

    归约

    // 终端操作的返回值
    // allMatch boolean
    // forEach void
    // findAny Optional<T>
    // collect R eg:List<T>
    
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    // 元素求和,有初始值的情况
    int sum = numbers.stream()
        .reduce(0, Integer::sum);
    System.out.println(sum);
    
    // 元素求和,无初始值
    Optional<Integer> sum2 = numbers.stream()
        .reduce(Integer::sum);
    System.out.println(sum2.get());
    
    // 最大值
    int maxValue = numbers.stream().reduce(0, Integer::max);
    System.out.println(maxValue);
    
    // 最小值
    int minValue = numbers.stream().reduce(0, Integer::min);
    System.out.println(minValue);
    
    // 菜单中菜的个数
    int dishCount = FakeDb.getMenu().stream()
        .map(d -> 1)
        .reduce(0, (a, b) -> a + b);
    System.out.println(dishCount);
    // 内置count
    System.out.println(FakeDb.getMenu().stream().count());
    

    无状态操作:诸如map或filter等操作会从输入流中获取每一个元素,并在输出流中得到0或1个结果。

    有状态操作:从流中排序和删除重复项时都需要知道先前的历史

    归约应用举例

    List<Transaction> tList = FakeDb.getTransactions();
    //    (1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。
    List<Transaction> list01 = tList.stream()
        .filter(t -> t.getYear() == 2011) // 2011年的交易
        .sorted(Comparator.comparing(Transaction::getValue)) // 交易额从低到高
        .collect(Collectors.toList());
    System.out.println(list01);
    
    //    (2) 交易员都在哪些不同的城市工作过?
    List<String> cityList = tList.stream()
        .map(t -> t.getTrader().getCity()) // 交易员所在城市
        .distinct() // 去重
        .collect(Collectors.toList());
    System.out.println(cityList);
    
    //    (3) 查找所有来自于剑桥的交易员,并按姓名排序。
    List<Trader> list3 = tList.stream()
        .map(Transaction::getTrader) // 交易员
        .filter(t -> "Cambridge".equals(t.getCity())) // 来自剑桥
        .distinct() // 去重
        .sorted(Comparator.comparing(Trader::getName)) // 按姓名排序
        .collect(Collectors.toList());
    System.out.println(list3);
    
    //    (4) 返回所有交易员的姓名字符串,按字母顺序排序。
    String nameStr = tList.stream()
        .map(t -> t.getTrader().getName()) // 交易员的姓名
        .distinct() // 去重
        // .sorted(Comparator.comparing(String::toString)) // 排序
        .sorted() // 排序,可以简写
        // .collect(Collectors.toList());
        // .reduce("", (n1, n2) -> n1 + n2 + " "); // 这种写法效率不高
    	.collect(Collectors.joining(" "));
    System.out.println(nameStr);
    
    //    (5) 有没有交易员是在米兰工作的?
    //        Optional<String> optional5 = tList.stream()
    //                .map(Transaction::getTrader)
    //                .map(Trader::getCity)
    //                .filter(city -> "Milan".equals(city))
    //                .findAny();
    //        System.out.println(optional5.isPresent());
    boolean milanBased = tList.stream()
        .anyMatch(t -> t.getTrader().getCity().equals("Milan"));
    System.out.println(milanBased);
    
    //    (6) 打印生活在剑桥的交易员的所有交易额。
    //        Optional<Integer> optional6 = tList.stream()
    //                .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
    //                .map(Transaction::getValue)
    //                .reduce((a, b) -> a + b);
    //        System.out.println(optional6.get());
    tList.stream()
        .filter(t -> "Cambridge".equals(t.getTrader().getCity()))
        .map(Transaction::getValue)
        .forEach(System.out::println);
    
    //    (7) 所有交易中,最高的交易额是多少?
    Optional<Integer> optional7 = tList.stream()
        .map(Transaction::getValue)
        .reduce(Integer::max);
    System.out.println(optional7.get());
    
    //    (8) 找到交易额最小的交易。
    Optional<Integer> optional8 = tList.stream()
        .map(Transaction::getValue)
        .reduce(Integer::min);
    System.out.println(optional8.get());
    

    数值流与对象流

    // 计算菜单中的卡路里
    int calories = FakeDb.getMenu().stream()
        .map(Dish::getCalories)
        .reduce(0, Integer::sum);
    // 它有一个暗含的装箱成本,每个Integer都必须拆箱成一个原始类型,再进行求和
    System.out.println(calories);
    
    // 原始类型流特化
    // IntStream,DoubleStream,LongStream分别将流中的元素特化为int,long,double,从而避免暗含的装箱成本
    
    // 映射到数值流
    int sum = FakeDb.getMenu().stream() // 返回 Stream<Dish>
        .mapToInt(Dish::getCalories) // 返回 IntStream
        .sum(); // sum,max,min,average
    // sum,如果流是空的,sum默认返回0
    System.out.println(sum);
    
    // 转换回对象流
    FakeDb.getMenu().stream()
        .mapToInt(Dish::getCalories)
        .boxed(); // 转换为Stream<Integer>
    
    // 默认值OptionalInt OptionalDouble OptionalLong
    // sum有默认值0,max,min,average没有默认值
    OptionalInt maxCalories = FakeDb.getMenu().stream()
        .mapToInt(Dish::getCalories)
        .max();
    System.out.println(sum);
    

    数值范围

    // [1, 100)
    IntStream numbers = IntStream.range(1, 100);
    // [1 100]
    IntStream numbers2 = IntStream.rangeClosed(1, 100);
    
    // 求勾股数
    IntStream.rangeClosed(1, 100).boxed()
        .flatMap(a -> IntStream.rangeClosed(a, 100)
                 .filter(b -> Math.sqrt(a*a + b*b )%1==0)
                 .mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a * a + b * b)}))
        .forEach(a -> System.out.println(a[0] + ", " + a[1] + ", " + a[2]));
    
    // 求勾股数 优化
    IntStream.rangeClosed(1, 100).boxed()
        .flatMap(a -> IntStream.rangeClosed(a, 100)
                 .mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)}))
        .filter(t -> t[2] % 1 == 0)
        .forEach(a -> System.out.println(a[0] + ", " + a[1] + ", " + a[2]));
    

    生成流

    // 由值创建流
    Stream.of("java 8", "Lambdas", "In", "Action")
        .map(String::toUpperCase)
        .forEach(System.out::println);
    
    // 由数组创建流
    int[] numbers = {2, 3, 5, 7, 11, 13};
    int sum = Arrays.stream(numbers).sum();
    System.out.println(sum);
    
    // 由文件生成流:统计文件中的不同单词数
    long uniqueWords = 0;
    try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
    
        uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
            .distinct()
            .count();
    
    } catch (IOException e) {
        e.printStackTrace();
    };
    System.out.println(uniqueWords);
    
    // 由函数生成流  无限流
    // Stream.iterate
    // Stream.generate
    Stream.iterate(0, n -> n + 2)
        .limit(10)
        .forEach(System.out::println);
    
    // 迭代:斐波纳契元组数列
    Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
        .limit(20)
        .forEach(t -> System.out.println("(" + t[0] + ", " + t[1] + ")"));
    
    // 生成
    Stream.generate(Math::random)
        .limit(5)
        .forEach(System.out::println);
    
    // 生成一个全是1的无限流
    IntStream.generate(() -> 1).limit(11).forEach(System.out::println);
    
    // 生成:斐波纳契元组数列
    IntStream.generate(new IntSupplier() {
        private int previous = 0;
        private int current = 1;
        @Override
        public int getAsInt() {
            int oldPrevious = this.previous;
            int nextValue = this.previous + this.current;
            this.previous = this.current;
            this.current = nextValue;
            return oldPrevious;
        }
    }).limit(11).forEach(System.out::println);
    

    其他,备注

    遍历数组取索引值

    https://stackoverflow.com/questions/18552005/is-there-a-concise-way-to-iterate-over-a-stream-with-indices-in-java-8

    https://www.zhihu.com/question/51841706

    遍历数组取索引值

    // import static java.util.stream.Collectors.toList;
    
    String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
    IntStream.range(0, names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(toList()); // List<String>
    
    // 方案一:
    IntStream.range(0, names.length)
        .forEach(i -> System.out.println(i + ", " + names[i]));
    
    // 方案二:
    AtomicInteger index = new AtomicInteger();
    List<String> list = Arrays.stream(names)
        .filter(n -> n.length() <= index.incrementAndGet())
        .collect(Collectors.toList());
    
    // 方案三:使用Guava
    Streams.mapWithIndex(Arrays.stream(names)
                         ,(str, index) -> index + ", " + str)
        .forEach(System.out::println);
    
    // 方案四:未看懂
    Seq.seq(Stream.of(names)).zipWithIndex()
        .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
        .toList();
    
    // 方案五:未看懂
    LazyFutureStream.of(names)
                     .zipWithIndex()
                     .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                     .toList();
    // 方案六:
    int[] idx = {-1};
    Arrays.stream(names)
        .forEach(i -> System.out.println(++idx[0] + ", " + names[idx[0]]));
    

    第三方库

    Guava

    Apache

    lambdaj

    Joda-Time

  • 相关阅读:
    遗传算法求解TSP问题
    蚁群算法求解TSP问题
    遗传算法之函数优化
    k-means和iosdata聚类算法在生活案例中的运用
    MATLAB实现模糊控制
    感知机算法及BP神经网络
    K-means聚类算法
    链表_leetcode92
    链表_leetcode86
    链表_leetcode83
  • 原文地址:https://www.cnblogs.com/okokabcd/p/9480338.html
Copyright © 2020-2023  润新知