zoukankan      html  css  js  c++  java
  • 024:Java流实现Shell:cat 1.log | grep a | sort | uniq -c | sort -rn

    640?wx_fmt=jpeg

    本文阅读时间大约13分钟(本文实践性很强,建议pc端阅读,最好亲自实践)。


    参考答案


    这个问题考察的是对Linux命令的熟悉程度,以及对Java中集合操作的综合运用,自从转到Java 8以后,我就一直使用流来处理集合了,下面的代码就是我用流来实现的参考答案:

    package org.java.learn.java8.stream;	
    	
    import java.io.*;	
    import java.util.*;	
    import java.util.function.Function;	
    import java.util.stream.Collectors;	
    	
    public class ShellExample {	
    	
        public static void main(String[] args) throws IOException {	
            //cat命令,相当于是读取文件中的所有行,并输出	
            File file = new File("/Users/duqi/IdeaProjects/LearnJava/src/main/java/org/java/learn/java8/stream/t1.txt");	
            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));	
            List<String> lines = new ArrayList<>();	
            String str = null;	
            while ((str = bufferedReader.readLine()) != null) {	
                lines.add(str);	
            }	
    	
            //grep a,相当于filter	
            lines = lines.stream().filter(s -> s.contains("a")).collect(Collectors.toList());	
    	
            //sort 按照字典序从小到大排序	
            lines = lines.stream().sorted().collect(Collectors.toList());	
    	
            //uniq -c,统计相同的元素的个数	
            Map<String, Long> integerMap =	
                    lines.stream().sorted().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));	
    	
            //sort -rn,排序后逆序输出	
            List<Long> res = integerMap.values().stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());	
    	
            res.forEach(System.out::println);	
        }	
    }


    知识点梳理


    背景&基本概念

    在以前,要操作一个集合,按照Java作为命令式语言的特点,开发者需要自己去关心集合的循环,每个循环里针对元素的操作(过滤、转换、合并)等等,这些代码写起来很繁琐,又容易出错。


    流(stream)是Java API的新成员,它允许开发者以声明方式处理集合(类似于写SQL),开发者只需要直接指明自己要做什么操作,而不需要关心对集合的迭代。使用流写出来的代码可读性很好、表达能力很强,我目前在开发中,能使用流的地方一定会使用流,它帮助我减少了很多代码行数。


    流也需要对集合做迭代,只是JDK的开发者将迭代放在了API背后,称为内部迭代,而集合的迭代则需要开发者自己维护,称为外部迭代。使用内部迭代的好处,一方面开发者的代码得以简化,另一方面,流可以在内部对迭代进行种种优化,同时不影响开发者的业务代码。


    常见api

    流的API分为两种,中间操作和终端操作,中间操作产生的结果还是一个流,终端操作产生的结果可能是一个集合或者是一个数字,总之不是一个流。


    640?wx_fmt=png


    常见的流的操作有:筛选(filter)、切片(limit)、映射(map、flatMap)、查找(find)、匹配(match)和规约(reduce);流不仅支持串行操作,还支持并行操作,使用并行流可以提高处理超大集合时候的性能。这里我整理了自己在工作中常用的流操作:


    操作类型返回类型使用的类型/函数式接口函数描述符
    filter中间StreamPredicateT -> boolean
    distinct中间Stream

    skip中间Streamlong
    limit中间Streamlong
    map中间StreamFunction<T, R>T -> R
    flatMap中间StreamFunction<T, Stream>T -> Stream
    sorted中间StreamComparator(T, T) -> int
    anyMatch终端booleanPredicateT -> boolean
    noneMatch终端booleanPredicateT -> boolean
    allMatch终端booleanPredicateT -> boolean
    findAny终端Optional

    findFirst终端Optional

    forEach终端voidConsumerT -> void
    collect终端RCollector<T, A, R>
    reduce终端OptionalBinaryOperator(T, T) -> T
    count终端Optional



    使用案例

    假设有交易和交易员两个概念——分别是下面的Trader和Transaction,现在有个交易列表,里面记录了这些交易员在某些年份的交易。

    首先,看交易员的定义

    package stream;	
    	
    import lombok.AllArgsConstructor;	
    import lombok.Data;	
    import lombok.NoArgsConstructor;	
    	
    /**	
    @Data	
    @AllArgsConstructor	
    @NoArgsConstructor	
    public class Trader {	
        private String name;	
        private String city;	
    }

    然后,看交易的定义

    package stream;	
    	
    import lombok.AllArgsConstructor;	
    import lombok.Builder;	
    import lombok.Data;	
    import lombok.NoArgsConstructor;	
    	
    /**	
    @Data	
    @AllArgsConstructor	
    @NoArgsConstructor	
    public class Transaction {	
        private Trader trader;	
        private int year;	
        private int value;	
    }

    上下文,有一个交易列表

    package stream;	
    	
    import java.util.Arrays;	
    import java.util.List;	
    	
    public class StreamExample {	
        public static void main(String[] args) {	
            Trader raoul = new Trader("Raoul", "Cambridge");	
            Trader mario = new Trader("Mario", "Milan");	
            Trader alan = new Trader("Alan", "Cambridge");	
            Trader brian = new Trader("Brian", "Cambridge");	
    	
            List<Transaction> transactions = Arrays.asList(	
                new Transaction(brian, 2011, 300),	
                new Transaction(raoul, 2012, 1000),	
                new Transaction(raoul, 2011, 400),	
                new Transaction(mario, 2012, 710),	
                new Transaction(mario, 2012, 700),	
                new Transaction(alan, 2012, 950)	
            );	
        }	
    }

    基于上述背景,这里将给出如下练习:

    • 找出2011年所有的交易并按照交易额排序(从低到高)

    List<Transaction> transactions2011 = transactions.stream()	
        .filter(transaction -> transaction.getYear() == 2011) //过滤出所有2011年的交易	
        .sorted(Comparator.comparing(Transaction::getValue))    //按照交易的金额排序	
        .collect(Collectors.toList());  //将所有的结果整理成列表

    • 交易员都在哪些不同的城市工作过

    List<String> cities = transactions.stream()            	
        .map(transaction -> transaction.getTrader().getCity())	
        .distinct()	
        .collect(Collectors.toList());

    • 查找所有来自Cambridge的交易员,并按照姓名排序

    List<Trader> traders = transactions.stream()	
        .filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity()))	
        .map(Transaction::getTrader)	
        .sorted(Comparator.comparing(Trader::getName))	
        .collect(Collectors.toList());

    • 将所有交易员的姓名按照字母顺序排序,并连接成一个字符串返回

    String nameStr = transactions.stream()	
        .map(transaction -> transaction.getTrader().getName())	
        .distinct()	
        .sorted()	
        .collect(Collectors.joining());

    • 有没有交易员是在Milan工作的?

    boolean milanBased = transactions.stream()	
        .anyMatch(transaction -> "Milan".equals(transaction.getTrader().getCity()));
    • 打印所有城市在剑桥的交易员的交易额

    transactions.stream()	
        .filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity()))	
        .map(Transaction::getValue)	
        .forEach(System.out::println);

    • 所有交易中,最高的交易额是多少?

    Optional<Integer> maxValue = transactions.stream()	
        .map(Transaction::getValue)	
        .reduce(Integer::max);

    • 将所有的交易按照年份分组,存放在一个Map中

    Map<Integer, Transaction> yearMap = transactions.stream()	
        .collect(Collectors.toMap(Transaction::getYear, transaction -> transaction));	
    
    • 找到交易额最小的交易

    Optional<Transaction> minTransaction = transactions.stream()	
        .min(Comparator.comparing(Transaction::getValue));	
    


    参考资料


    1. https://www.journaldev.com/2774/java-8-stream

    2. 《Java 8实战》

    3. https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#package.description

     你再主动一点点 640?  我们就有故事了

    本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

    640

    扫码关注

    有趣的灵魂在等你

    640?wx_fmt=jpeg640下方查看历史文章640?wx_fmt=png

    023:接口和抽象类有什么区别?

    022:如果要将对象用作Map中的key,需要注意什么

    021:谈谈面向对象的三大特性

    020:举几个String的API以及案例

    640在看点一下
  • 相关阅读:
    POJ3320 Jessica's Reading Problem
    POJ3320 Jessica's Reading Problem
    CodeForces 813B The Golden Age
    CodeForces 813B The Golden Age
    An impassioned circulation of affection CodeForces
    An impassioned circulation of affection CodeForces
    Codeforces Round #444 (Div. 2) B. Cubes for Masha
    2013=7=21 进制转换
    2013=7=15
    2013=7=14
  • 原文地址:https://www.cnblogs.com/javaadu/p/11742610.html
Copyright © 2011-2022 走看看