当前位置:首页>编程日记>正文

震惊 Guava 竟然有坑

点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

转自:Java布道

最近,团队里边一个兄弟突然叫我:快来看,有个奇怪的事情,无法解释…跑过去一看,是这么一段代码:

 private static class Person {private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return name + ":" + age;}}@Testpublic void test_collections2_filter() {Person lxy = new Person(35, "lxy");Person xhf = new Person(34, "xhf");Person nws = new Person(31, "nws");List<Person> names = Lists.newArrayList(lxy, xhf, nws);Collection<Person> personAgeOver30 = Collections2.filter(names, p -> p.age > 30);System.out.println(personAgeOver30);//[lxy:35, xhf:34, nws:31]nws.setAge(25);System.out.println(personAgeOver30);//[lxy:35, xhf:34]}

确实是比较奇怪,personAgeGt30中的元素怎么会少了一个呢?本着任何表现奇怪的程序都有其背后原因的指导思想,打开了Guava Collections2类的源代码。其实,源代码的注释已经解释得非常清楚了:returned collection is a live view of {@code unfiltered};changes to one affect the other.

/*** Returns the elements of {@code unfiltered} that satisfy a predicate. The returned collection is* a live view of {@code unfiltered}; changes to one affect the other.** <p>The resulting collection's iterator does not support {@code remove()}, but all other* collection methods are supported. When given an element that doesn't satisfy the predicate, the* collection's {@code add()} and {@code addAll()} methods throw an {@link* IllegalArgumentException}. When methods such as {@code removeAll()} and {@code clear()} are* called on the filtered collection, only elements that satisfy the filter will be removed from* the underlying collection.** <p>The returned collection isn't threadsafe or serializable, even if {@code unfiltered} is.** <p>Many of the filtered collection's methods, such as {@code size()}, iterate across every* element in the underlying collection and determine which elements satisfy the filter. When a* live view is <i>not</i> needed, it may be faster to copy {@code Iterables.filter(unfiltered,* predicate)} and use the copy.** <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at* {@link Predicate#apply}. Do not provide a predicate such as {@code* Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link* Iterables#filter(Iterable, Class)} for related functionality.)** <p><b>{@code Stream} equivalent:</b> {@link java.util.stream.Stream#filter Stream.filter}.*/

Collections2.filter方法返回的只是原有列表的一个视图。所以:改变被过滤列表会影响过滤后列表,反之亦然。并且我们从这段文字中还能get到下面几个注意事项:

  1. 过滤后列表的迭代器不支持remove()操作

  2. add不符合过滤条件的元素,会抛出IllegalArgumentException

  3. 当过滤后列表中的元素不再满足过滤条件时,会影响到已经过滤出来的列表出于程序员的本能,决定还是看下源码心里更踏实。核心源码如下:

1. add方法:public boolean add(E element) {//不符合过滤条件抛IllegalArgumentException的原因checkArgument(predicate.apply(element));return unfiltered.add(element);}不支持通过迭代器删除的原因:public abstract class UnmodifiableIterator<E> implements Iterator<E> {/** Constructor for use by subclasses. */protected UnmodifiableIterator() {}/*** Guaranteed to throw an exception and leave the underlying data unmodified.*
* @throws UnsupportedOperationException always
* @deprecated Unsupported operation.*/@Deprecated@Overridepublic final void remove() {throw new UnsupportedOperationException();}打印结果变化的原因:public String toString() {Iterator<E> it = iterator();if (! it.hasNext())return "[]";StringBuilder sb = new StringBuilder();sb.append('[');for (;;) {E e = it.next();sb.append(e == this ? "(this Collection)" : e);if (! it.hasNext())return sb.append(']').toString();sb.append(',').append(' ');}}@Overridepublic Iterator<E> iterator() {return Iterators.filter(unfiltered.iterator(), predicate);}

问题已经弄清楚了,怎么修改这个问题呢?

方案一(可行):

干脆不用guava,

List<Person> names = Lists.newArrayList(lxy, xhf, nws);ArrayList<Person> persons = new ArrayList<>();for (Person p : names) {if(p.age > 30) {persons.add(p);}
}

方案二(可行):

用个容器再包装一下:

Collection<Person> personAgeGt30 = new ArrayList<(Collections2.filter(names, p -> p.age > 30));

方案三(可行,改用Java8的过滤器):

List<Person> personAgeGt30 = names.stream().filter((Predicate<Person>) p -> p.age >30).collect(Collectors.toList());

方案四(可行,改用Guava的连贯接口,IDEA编辑器会提示你替换成Java8 API)

ImmutableList<Person> personAgeGt30 = FluentIterable.from(names).filter(p -> p.age > 30).toList();

上述方案中,支持java8的生产环境推荐方案三,不支持java8的生产环境推荐方案二。

总结

其实,Java语言中类似的坑还有很多,比如:

  • 1.Arrays.asList()生成的列表是不可变的。

  • 2.subList生成的子列表,对原列表元素的修改,会导致子列表的遍历、增加、删除抛出ConcurrentModificationException,

  • 3.subList对子列表的修改会影响到原列表数据

  • 4.修改Map的keySet()方法和values()生成的容器,都会影响Map本身。

总之,使用任何API之前多看看源码,至少看看源码的注释。

热门内容:
  • Java身份证号码识别系统

  • 看看人家那后端API接口写得,那叫一个优雅!

  • 给IDEA换个酷炫的主题,这个有点哇塞啊!

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

http://www.coolblog.cn/news/5732e34dd0149692.html

相关文章:

  • asp多表查询并显示_SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询
  • s7day2学习记录
  • 【求锤得锤的故事】Redis锁从面试连环炮聊到神仙打架。
  • 矿Spring入门Demo
  • 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
  • Linux 实时流量监测(iptraf中文图解)
  • Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置
  • 美颜
  • shell访问php文件夹,Shell获取某目录下所有文件夹的名称
  • 如何优雅的实现 Spring Boot 接口参数加密解密?
  • LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络
  • Mac原生Terminal快速登录ssh
  • java受保护的数据与_Javascript类定义语法,私有成员、受保护成员、静态成员等介绍...
  • mysql commit 机制_1024MySQL事物提交机制
  • 支撑微博千亿调用的轻量级RPC框架:Motan
  • jquery 使用小技巧
  • 2019-9
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010
  • vscode pylint 错误_将实际未错误的py库添加到pylint白名单
  • 科学计算工具NumPy(3):ndarray的元素处理
  • 工程师在工作电脑存 64G 不雅文件,被公司开除后索赔 41 万,结果…
  • linux批量创建用户和密码
  • newinsets用法java_Java XYPlot.setInsets方法代碼示例
  • js常用阻止冒泡事件
  • 气泡图在开源监控工具中的应用效果
  • 各类型土地利用图例_划重点!国土空间总体规划——土地利用
  • php 启动服务器监听
  • dubbo简单示例
  • 【设计模式】 模式PK:策略模式VS状态模式
  • [iptables]Redhat 7.2下使用iptables实现NAT
  • Ubuntu13.10:[3]如何开启SSH SERVER服务
  • CSS小技巧——CSS滚动条美化
  • JS实现-页面数据无限加载
  • 阿里巴巴分布式服务框架 Dubbo
  • 最新DOS大全
  • Django View(视图系统)
  • 阿里大鱼.net core 发送短信
  • 程序员入错行怎么办?
  • 两张超级大表join优化
  • 第九天函数
  • Linux软件安装-----apache安装
  • HDU 5988 最小费用流
  • Sorenson Capital:值得投资的 5 种 AI 技术
  • 《看透springmvc源码分析与实践》读书笔记一
  • 正式开课!如何学习相机模型与标定?(单目+双目+鱼眼+深度相机)
  • Arm芯片的新革命在缓缓上演
  • nagios自写插件—check_file
  • python3 错误 Max retries exceeded with url 解决方法
  • 行为模式之Template Method模式
  • 通过Spark进行ALS离线和Stream实时推荐