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

回复“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 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

震惊 Guava 竟然有坑相关推荐

  1. 警惕,MyBatis的size()方法竟然有坑!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://h5ip.cn/aJgJ Mybatis是一个 ...

  2. 震惊,竟然有人用Taro来。。。

    基于taro开发了个仿小红书的应用 学react学了几个月了,对node也有一点了解,心里总想做点什么,于是和一个关系比较不错的同事决定做点小玩意.我心想程序员周末总是宅在家有点无聊,如果有一个朋友间 ...

  3. 警惕 | 警惕,mybatis的size()方法竟然有坑!

    Hi ! 我是小小,今天我们又见面了,今日的主要内容是MyBatis的size方法使用的主要的注意事项. 前言 MyBatis 是一个开源的轻量级的半自动化的 ORM 框架,用于面向对象和关系型数据库 ...

  4. 震惊:竟然有大神做出了地府后台管理系统!!!

    之前有一个有趣的段子是这样的: 没想到真的有大神把这东西实现了!! 我带大家看一下这个地府管理系统: 登陆页面支持多种登陆方式,可以看到左边侧栏,供阎王爷用的功能还挺全哈. 下面是地府大数据可视化平台 ...

  5. 震惊,竟然能通过表达式计算VO,再也不用手打Getter计算方法了,程序员福利

    我有一个问题: 假设我们从给前端返回一个VO,但是VO中的某些属性需要通过计算得来的.如果我们每次都要在实体类中直接计算的话,不利于代码维护性,每次改变计算方法都要重新修改方法,十分麻烦.所以我就想能 ...

  6. 讨论:Service层需要接口吗?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 链接:toutiao.com/i6882356844245 ...

  7. 没想到 | 万万没想到 Java 中最重要的关键字竟然是这个

    Hi! 我是小小,今天是本周的第六篇,主要内容是关于 volatile 关键字. 前言 volatile 关键字主要是用于指令重排序,常常用于保证内存的可见性和防止指令重排序. 保证内存可见性 内存可 ...

  8. Android target sdk 31升级采坑

    最近发布Google 内侧版本app时老是会出现一个警告⚠️ "app的target sdk 版本必须不能低于31(Android 12)" 否则今年12月之后将影响上架.于是趁着 ...

  9. Python3.X 爬虫实战(静态下载器与解析器)

    [工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 这两天比较忙,各种锅锅接,忙里偷闲完结这一篇吧.在我们在上一篇&l ...

最新文章

  1. 专题 4 通用函数库之调试功能封装
  2. QML编程之旅 之可视元素
  3. xcode开发html5工具,5个Xcode开发调试技巧
  4. 获得创建临时表的session id
  5. 初学关键段容易忽略的问题
  6. 职业学校计算机教师履职总结,中等职业学校骨干教师(计算机应用专业)省级培训 总结...
  7. 8086的总线操作顺序
  8. multisim安装完成后显示安装程序损坏的免费解决方案
  9. 微模块、冷通道监控系统解决方案
  10. 计算机里没有usb驱动设备,USB驱动,电脑没有usb驱动怎么办
  11. python写的一个王者荣耀刷金币脚本
  12. 使用backdrop-filter实现毛玻璃效果
  13. 基于上下文的业务流建模法(三)
  14. 2021-2027全球及中国远红外桑拿行业研究及十四五规划分析报告
  15. wps word打开是html,用Word打开WPS文件的两种方法,WPS文件如何打开?
  16. 20162303 结对编程项目-四则运算 第一周输出阶段总结博客
  17. 一文详解AIGC:推动元宇宙发展的加速器
  18. 一个tab标签效果类
  19. 重回童年4399| 【黄金矿工】游戏制作+解析
  20. 设计模式 -- 访问者模式(Visitor Pattern)

热门文章

  1. Vue报错:Uncaught RangeError: Maximum call stack size exceeded
  2. Qt 在Label上面绘制罗盘
  3. codeforces 8C. Looking for Order 状压dp
  4. mvc-3模型和数据(1)
  5. 使用 fcntl 函数 获取,设置文件的状态标志
  6. [PyQt4]项目开发中遇到的错误与解决办法
  7. OLE 操作Excel 详解(转)
  8. 【组队学习】【31期】IOS开发
  9. 谷歌 AI 编舞师,连张艺兴最喜欢的 Krump 都不在话下
  10. 百度CTO王海峰阐释AI融合创新,降低门槛,按下产业智能化加速键