JAVA编程艺术之guava应用

数据处理函数编程之Guava

最近在开发宝贝化项目过程中,遇到一个问题:同一个车型对应多种价格,需要做过滤处理,保留统一车型最低的价格;看似很简单的问题,但如何优雅的编程,让代码更加简洁,性能更好。对于有代码洁癖的人来说,想到了guava强大的函数处理功能以及Jdk8 stream语法;项目结束之后,想把这个guava函数编程应用总结分享一下

  • guava的应用场景
  • guava排序功能
  • guava过滤功能
  • jdk8的lambda表达式
  • jdk8的Stream语法

guava的应用场景

前后端分离技术体系redux的应用场景有过这么一句话:当你不知道什么时候用到redux的时候,那就是不需要它。其实在java这种面向对象的编程思想,很少会考虑到函数式编程,主要在于函数式编程不符合面向对象的思想,难以理解,甚至有时候代码阅读非常困难,对于代码的维护那是一件灾难性的事件。所以一般场景不太建议大家用函数式编程。但有些场景比如复杂的数据处理,算法运算等等,这些可以考虑用到guava的函数编程思想。

排序功能

排序器Ordering是guava流畅风格排序器Comparator的模板实现,用它可以构建复杂的集合排序功能

《一》常见的排序器

方法功能
natural()对排序类型自然排序:数字大小、日期先后顺序等
usingToString()对字符串进行字典排序
arbitrary()无序,每次排序的结果都不相同

实现自定义排序器,通常可以使用Ordering.from(Comparator)的方式,或者实现Ordering模板构造

Comparator<String> vr = (s1, s2) -> BigDecimal.valueOf(Double.valueOf(s1)).compareTo(BigDecimal.valueOf(Double.valueOf(s2)));
TreeMultimap<Long, String> keyMap = TreeMultimap.create(Ordering.natural(), Ordering.from(vr));

示例:

List<String> tlist = Lists.newArrayList(ImmutableList.of("welcome","to","guava","java","lambda"));
System.out.println("排序前:" + JSON.toJSONString(tlist));
//排序前:["welcome","to","guava","java","lambda"]
tlist =  Ordering.usingToString().sortedCopy(tlist);
System.out.println("排序后" + JSON.toJSONString(tlist));
//排序后["guava","java","lambda","to","welcome"]

《二》链式调用方法:通过链式调用,可以由给定的排序器衍生出其它排序器

方法功能
reverse()获取语义相反的排序器
nullsFirst()把null值排在最前面
nullsLast()把null值排在最后面
compound(Comparator)合成另一个比较器,以处理当前排序器中的相等情况
onResultOf(Function)对集合中元素调用Function,再按返回值用当前排序器排序

示例:

对PriceInfo这个类,对车型carType自然排序,null值排在前面(纯属为了说明用法)

@Data
public class PriceInfo {

    /**
     * 车型
     */
    private Long carType;

    /**
     * 价格
     */
    private String price;

}

实现方式:

Function<PriceInfo, Long> sortCarTypeFunction = new Function<PriceInfo, Long>() {
            @Override
            public Long apply(PriceInfo priceInfo) {
                return priceInfo.getCarType();
            }
        };
Ordering<PriceInfo> carTypeOrdering = Ordering.natural().reverse().nullsFirst().onResultOf(sortCarTypeFunction);

过滤功能

我们可以使用com.google.common.collect.Iterables和com.google.common.base.Predicates类来过滤例子中的列表

 List<String> filterList = Lists.newArrayList("welcome", "to", "guava", "java", "lambda");
        Predicate<String> lessThenPredicate = new Predicate<String>() {
            @Override
            public boolean apply(String s) {
                return s.length() > 5;
            }
        };
Iterable<String> filterResult = Iterables.filter(filterList, Predicates.or((Predicates.or(Predicates.equalTo("guava"), Predicates.equalTo("java"))), lessThenPredicate));
       Preconditions.checkArgument(Lists.newArrayList(filterResult).containsAll(Lists.newArrayList("guava", "java", "to")), "集合包含:[guava,welcome,lambda,java]");

Jdk8的Stream语法以及Lambda表达式应用

API地址:https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

jdk8引入stream语法主要是为了弥补函数式编程的缺陷,实际上也是借鉴了guava的函数编程风格;stream和I/O不同,它更像具有Iterable的集合类,但行为和集合类又有所不同。整体来讲Stream API包含构建、中间操作、终端操作;

《一》Stream构建

  1. 使用Stream静态方法来创建Stream:主要有of、generator、iterate方法
  2. 通过Collection子类获取Stream

《二》Stream的中间操作

  1. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;
  2. map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;
  3. limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

《三》Stream的终端操作

  1. collect:可变汇聚方法:
List<PriceInfo> result = list.stream().filter(priceInfo -> {
            Long carType = priceInfo.getCarType();
            if (null != keyMap.get(carType) && keyMap.get(carType).size() > 1) {
                return keyMap.get(carType).first().equals(priceInfo.getPrice());
            }
            return true;
        }).collect(Collectors.toList());
  1. reduce操作:比如count、sum等函数

附:对同一个车型进行过滤,保留最低价格,代码示例:

 PriceInfo p1 = new PriceInfo();
 p1.setCarType(2L);
 p1.setPrice("23");
 PriceInfo p2 = new PriceInfo();
 p2.setCarType(3L);
 p2.setPrice("20");
 PriceInfo p3 = new PriceInfo();
 p3.setCarType(2L);
 p3.setPrice("19");
 PriceInfo p4 = new PriceInfo();
 p4.setCarType(2L);
 p4.setPrice("30");
 List<PriceInfo> list = Lists.newArrayList(ImmutableList.of(p1, p2, p3, p4));
 Comparator<String> vr = (s1, s2) ->   BigDecimal.valueOf(Double.valueOf(s1)).compareTo(BigDecimal.valueOf(Double.valueOf(s2)));
TreeMultimap<Long, String> keyMap = TreeMultimap.create(Ordering.natural(), Ordering.from(vr));
        list.stream().forEach(priceInfo -> {
            keyMap.put(priceInfo.getCarType(), priceInfo.getPrice());
        });
        List<PriceInfo> result = list.stream().filter(priceInfo -> {
            Long carType = priceInfo.getCarType();
            if (null != keyMap.get(carType) && keyMap.get(carType).size() > 1) {
                return keyMap.get(carType).first().equals(priceInfo.getPrice());
            }
            return true;
        }).collect(Collectors.toList());

更多Stream功能了解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

内容简介 本书揭示了Java程序员如何创建高质量软件的奥秘。Herbert Schildt 和James Holmes两位编程大师通过将Java应用于某些有趣、实用的计算机问题和编程任务中,全面展示了Java语言的强大功能、敏捷性、多样性和艺术性。本书各章内容分别涉及到Java精髓、递归下降的表达式解析器、用Java实现语言解释器、用Java创建下载管理器、用Java实现E-mail客户端和Internet搜索、用Java修饰HTML、显示统计图表、金融应用中的Applet和Servlet、基于AI的问题求解等,每章给出的示例代码都可以直接运行,无需修改,而且读者可以从www.osborne.com站点上免费下载这些代码。本书是亚马逊网站上的五星级图书,相信读者能够通过研读本书大大提高自己的Java开发能力。本书代码示例易懂有趣,设计思想独特,定会使您受益匪浅! 目录 第1章 Java精髓 1 1.1 简单数据类型和对象:完美的平衡 2 1.2 通过垃圾回收实现内存管理 3 1.3 完美的简单多线程模型 3 1.4 完全集成的异常机制 4 1.5 对多态性支持的改进 5 1.6 通过字节码保证可移植性和安全性 5 1.7 丰富的Java API 6 1.8 Applet 7 1.9 继续变革 7 第2章 递归下降的表达式解析器 9 2.1 表达式 10 2.2 解析表达式 11 2.3 表达式的解析 11 2.4 表达式的分解 13 2.5 一个简单的表达式解析器 16 2.6 向解析器中添加变量 24 2.7 递归下降解析器中的语法检查 34 2.8 计算器Applet 34 2.9 一些尝试 37 第3章 使用Java实现语言解释器 38 3.1 解释何种计算机语言 39 3.2 解释器概述 40 3.3 Small BASIC解释器 41 3.4 Small BASIC表达式解析器 61 3.4.1 Small BASIC的表达式 61 3.4.2 Small BASIC的标识符 62 3.5 解释器 66 3.5.1 InterpreterException类 67 3.5.2 SBasic构造函数 67 3.5.3 关键字 68 3.5.4 run()方法 70 3.5.5 sbInterp()方法 71 3.5.6 赋值 72 3.5.7 PRINT语句 73 3.5.8 INPUT语句 75 3.5.9 GOTO语句 76 3.5.10 IF语句 78 3.5.11 FOR循环 79 3.5.12 GOSUB 82 3.5.13 END语句 83 3.6 Small BASIC的使用 83 3.7 对解释器进行增强和扩展 86 3.8 创建自己的计算机语言 86 第4章 使用Java创建下载管理器 88 4.1 深入理解Internet下载流程 89 4.2 下载管理器概述 89 4.3 Download类 90 4.3.1 Download类的变量 94 4.3.2 Download类的构造函数 94 4.3.3 download()方法 95 4.3.4 run()方法 95 4.3.5 stateChanged()方法 99 4.3.6 Action和Accessor方法 99 4.4 ProgressRenderer类 99 4.5 DownloadsTableModel类 100 4.5.1 addDownload()方法 103 4.5.2 clearDownload()方法 103 4.5.3 getColumnClass()方法 103 4.5.4 getValueAt()方法 104 4.5.5 update()方法 104 4.6 DownloadManager类 105 4.6.1 DownloadManager类的变量 111 4.6.2 DownloadManager类的构造函数 111 4.6.3 verifyUrl()方法 111 4.6.4 tableSelectionChanged()方法 112 4.6.5 updateButtons()方法 113 4.6.6 动作事件的处理 114 4.7 编译并运行下载管理器 114 4.8 改进下载管理器 115 第5章 用Java实现E-mail客户端 116 5.1 E-mail内幕 117 5.1.1 POP3 117 5.1.2 IMAP 117 5.1.3 SMTP 118 5.1.4 发送和接收E-mail的一般过程 118 5.2 JavaMail API 118 5.3 一个简单的E-mail客户端 120 5.3.1 ConnectDialog类 121 5.3.2 DownloadingDialog类 126 5.3.3 MessageDialog类 128 5.3.4 MessagesTableModel类 135 5.3.5 EmailClient类 139 5.4 编译并运行E-mail客户端 156 5.5 对基本E-mail客户端的扩展 157 第6章 使用Java搜索Internet 158 6.1 Web搜索程序基础 159 6.2 遵守Robot协议 160 6.3 Search Crawler概述 161 6.4 SearchCrawler类 162 6.4.1 SearchCrawler类的变量 179 6.4.2 SearchCrawler类的构造函数 179 6.4.3 actionSearch()方法 180 6.4.4 search()方法 182 6.4.5 showError()方法 185 6.4.6 updateStats()方法 185 6.4.7 addMatch()方法 186 6.4.8 verifyUrl()方法 187 6.4.9 isRobotAllowed()方法 188 6.4.10 downloadPage()方法 190 6.4.11 removeWwwFromUrl()方法 191 6.4.12 retrieveLinks()方法 192 6.4.13 searchStringMatches()方法 198 6.4.14 crawl()方法 199 6.5 Web搜索程序的编译与运行 202 6.6 对Web搜索程序的思考 203 第7章 使用Java提交HTML 205 7.1 使用JEditorPane提交HTML 206 7.2 处理超链接事件 206 7.3 编写Mini Web浏览器 207 7.3.1 MiniBrowser类 208 7.3.2 MiniBrowser类的变量 213 7.3.3 MiniBrowser类的构造函数 214 7.3.4 actionBack()方法 214 7.3.5 actionForward()方法 215 7.3.6 actionGo()方法 215 7.3.7 showError()方法 216 7.3.8 verifyUrl()方法 216 7.3.9 showPage()方法 217 7.3.10 updateButtons()方法 218 7.3.11 hyperlinkUpdate()方法 219 7.4 Mini Web浏览器的编译与运行 220 7.5 HTML提交程序的应用前景 221 第8章 统计. 图表与Java 222 8.1 样本. 总体. 分布与变量 223 8.2 基本统计值 223 8.2.1 平均数 224 8.2.2 中位数 224 8.2.3 众数 225 8.3 方差和标准差 227 8.4 回归方程式 228 8.5 Stats类的完整代码 232 8.6 绘图数据 235 8.6.1 数据的缩放 236 8.6.2 Graphs类 236 8.6.3 Graphs类的final变量和实例变量 241 8.6.4 Graphs类的构造函数 242 8.6.5 paint()方法 244 8.6.6 bargraph()方法 247 8.6.7 scatter()方法 247 8.6.8 regplot()方法 248 8.7 一个统计表的应用 248 8.7.1 StatsWin类的构造函数 253 8.7.2 itemStateChanged()事件处理器 254 8.7.3 actionPerformed()方法 255 8.7.4 shutdown()方法 255 8.7.5 createMenu()方法 255 8.7.6 DataWin类 255 8.7.7 联合使用 256 8.8 创建一个简单的统计Applet 258 8.9 一点尝试 260 第9章 金融应用中的Applet和Servlet 261 9.1 计算贷款的定期还款额 262 9.1.1 RegPay的成员 266 9.1.2 init()方法 266 9.1.3 actionPerformed()方法 269 9.1.4 paint()方法 269 9.1.5 compute()方法 270 9.2 计算投资的长期收益 270 9.3 计算达到某项长期收益目标所需的投资本金 274 9.4 计算达到某项期望年金所需的初始投资 278 9.5 根据给定投资计算年金的最大值 282 9.6 计算某项贷款的账户余额 286 9.7 创建用于金融类计算的Servlet 290 9.7.1 使用Tomcat 291 9.7.2 测试Servlet 291 9.7.3 把RegPay转换成Servlet 292 9.7.4 RegPayS程序 292 9.8 一些尝试 296 第10章 基于AI的问题求解 297 10.1 表示法和相关术语 298 10.2 组合爆炸问题 299 10.3 搜索技术 300 10.4 问题的描述 301 10.5 FlightInfo类 303 10.6 深度优先搜索 304 10.7 广度优先搜索 313 10.8 添加启发信息 316 10.8.1 爬山搜索 317 10.8.2 爬山搜索的分析 322 10.8.3 最小代价搜索 323 10.8.4 最小代价搜索的分析 324 10.9 查找多个解 324 10.9.1 路径删除 325 10.9.2 节点删除 326 10.10 查找最优解 331 10.11 回到丢失钥匙的例子 336
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值