024:用 Java 实现 shell 命令 cat 1.log | grep a | sort | uniq -c | sort -rn 的功能 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nkduqi
V2EX    Java

024:用 Java 实现 shell 命令 cat 1.log | grep a | sort | uniq -c | sort -rn 的功能

  •  
  •   nkduqi 2019-06-08 13:41:45 +08:00 2969 次点击
    这是一个创建于 2319 天前的主题,其中的信息可能已经有所发展或是发生改变。

    artificial-intelligence-codes-coding-247791.jpg

    参考答案

    这个问题考察的是对 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 分为两种,中间操作和终端操作,中间操作产生的结果还是一个流,终端操作产生的结果可能是一个集合或者是一个数字,总之不是一个流。 stream.png

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

    | 操作 | 类型 | 返回类型 | 使用的类型 /函数式接口 | 函数描述符 | | :-------: | :--: | :---------: | :--------------------: | :------------: | | filter | 中间 | Stream<t> | Predicate<t> | T -> boolean | | distinct | 中间 | Stream<t> | | | | skip | 中间 | Stream<t> | long | | | limit | 中间 | Stream<t> | long | | | map | 中间 | Stream<r> | Function<T, R> | T -> R | | flatMap | 中间 | Stream<r> | Function<T, Stream<r>> | T -> Stream<r> | | sorted | 中间 | Stream<t> | Comparator<t> | (T, T) -> int | | anyMatch | 终端 | boolean | Predicate<t> | T -> boolean | | noneMatch | 终端 | boolean | Predicate<t> | T -> boolean | | allMatch | 终端 | boolean | Predicate<t> | T -> boolean | | findAny | 终端 | Optional<t> | | | | findFirst | 终端 | Optional<t> | | | | forEach | 终端 | void | Consumer<t> | T -> void | | collect | 终端 | R | Collector<T, A, R> | | | reduce | 终端 | Optional<t> | BinaryOperator<t> | (T, T) -> T | | count | 终端 | Optional<t> | | |</t></t></t></t></t></t></t></t></t></t></t></r></r></r></r></t></t></t></t></t>

    使用案例

    假设有交易和交易员两个概念分别是下面的 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.journalde.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

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

    javaadu

    4 条回复    2019-06-09 10:36:10 +08:00
    leonme
        1
    leonme  
       2019-06-08 14:04:25 +08:00 via Android
    你推广就推广,下面 stream 流直接照搬 java8 in action 里的 demo 是几个意思,希望能有点自己的思考
    chendy
        2
    chendy  
       2019-06-08 14:49:37 +08:00   2
    看到二维码图裂了就很开心
    changdy
        3
    changdy  
       2019-06-09 07:12:39 +08:00
    好菜啊.java 现在还用这种方式读文件? 老哥走点心吧.
    ```java
    List<String> list = Files.readAllLines(Paths.get("README.md"));
    Collections.sort(list);
    ```
    CSM
        4
    CSM  
       2019-06-09 10:36:10 +08:00 via Android
    为什么全部不处理完后再 collect ?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5465 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 07:47 PVG 15:47 LAX 00:47 JFK 03:47
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86