Guava Lists.transform踩坑小记

article/2025/8/23 19:51:45

1.问题提出

1.前段时间在项目中用到Lists.transform返回的List,在对该list修改后发现修改并没有反映在结果里,研究源码后发现问题还挺大。
下面通过单步调试的结果来查看Guava Lists.transform使用过程中需要注意的地方。

a.对原有的list列表修改会影响Lists.transform已经生成列表


由上图可以看出,对原数据集personDbs的修改会直接影响到Lists.transform方法返回的结果personVos,
这是很危险的,如果在使用的过程中不注意的话会造成很严重的问题,而这种问题又是很隐蔽的,在项目中
无疑是个不定时的炸弹。
b.对Lists.transform生成的列表的元素进行修改可能无法生效


由上面的调试结果可以看出对Lists.transform返回的List列表中的元素的修改不会"生效",即修改不会反映在list列表中。
c.对returnList调用add、addAll和shuffle等修改returnList的方法会抛异常
对personVos调用Collections.shuffle(personVos);或personVos.add(personDbToVo(new PersonDb("sting", 30)));
都会抛出java.lang.UnsupportedOperationException。
附测试代码:

package com.google.common.base;import com.google.common.collect.Lists;
import org.junit.Test;import java.util.List;/*** @author mnmlist@163.com* @date 2016/12/23* @time 19:31*/
public class ListsTransformTest {public PersonVo personDbToVo(PersonDb personDb) {Preconditions.checkNotNull(personDb, "[PersonDbToVo]personDb为null");PersonVo personVo = new PersonVo();personVo.setName(personDb.getName() + ",from Db");personVo.setAge(personDb.getAge());personVo.setMsg(personDb.getMsg());return personVo;}@Testpublic void testListsTransform() {List<PersonDb> personDbs = Lists.newArrayList(new PersonDb("zhangsan", 20),new PersonDb("lisi", 24), new PersonDb("wangwu", 30));List<PersonVo> personVos = Lists.transform(personDbs, new Function<PersonDb, PersonVo>() {@Overridepublic PersonVo apply(PersonDb personDb) {return personDbToVo(personDb);}});for(PersonDb personDb : personDbs) {personDb.setMsg("hello world!");}//Collections.shuffle(personVos);//personVos = ImmutableList.copyOf(personVos);//personVos = Lists.newArrayList(personVos);for(PersonVo personVo : personVos) {personVo.setMsg("Merry Christmas!");}personVos.add(personDbToVo(new PersonDb("sting", 30)));System.out.println(personVos);}
}
class PersonDb {private String name;private int age;private String msg;public PersonDb(String name, int age){this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}@Overridepublic String toString() {return MoreObjects.toStringHelper(this).add("name", name).add("age", age).add("msg", msg).toString();}
}
class PersonVo {private String name;private int age;private String msg;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}@Overridepublic String toString() {return MoreObjects.toStringHelper(this).add("name", name).add("age", age).add("msg", msg).toString();}
}

2.源码解读和异常分析

带着上面的三个问题去查看源码

    /*** Returns a list that applies {@code function} to each element of {@code* fromList}. The returned list is a transformed view of {@code fromList};* changes to {@code fromList} will be reflected in the returned list and vice* versa.** <p>Since functions are not reversible, the transform is one-way and new* items cannot be stored in the returned list. The {@code add},* {@code addAll} and {@code set} methods are unsupported in the returned* list.** <p>The function is applied lazily, invoked when needed. This is necessary* for the returned list to be a view, but it means that the function will be* applied many times for bulk operations like {@link List#contains} and* {@link List#hashCode}. For this to perform well, {@code function} should be* fast. To avoid lazy evaluation when the returned list doesn't need to be a* view, copy the returned list into a new list of your choosing.** <p>If {@code fromList} implements {@link RandomAccess}, so will the* returned list. The returned list is threadsafe if the supplied list and* function are.** <p>If only a {@code Collection} or {@code Iterable} input is available, use* {@link Collections2#transform} or {@link Iterables#transform}.** <p><b>Note:</b> serializing the returned list is implemented by serializing* {@code fromList}, its contents, and {@code function} -- <i>not</i> by* serializing the transformed values. This can lead to surprising behavior,* so serializing the returned list is <b>not recommended</b>. Instead,* copy the list using {@link ImmutableList#copyOf(Collection)} (for example),* then serialize the copy. Other methods similar to this do not implement* serialization at all for this reason.*/@CheckReturnValuepublic static <F, T> List<T> transform(List<F> fromList, Function<? super F, ? extends T> function) {return (fromList instanceof RandomAccess)? new TransformingRandomAccessList<F, T>(fromList, function): new TransformingSequentialList<F, T>(fromList, function);}private static class TransformingRandomAccessList<F, T> extends AbstractList<T>implements RandomAccess, Serializable {final List<F> fromList;final Function<? super F, ? extends T> function;TransformingRandomAccessList(List<F> fromList, Function<? super F, ? extends T> function) {this.fromList = checkNotNull(fromList);this.function = checkNotNull(function);}@Overridepublic void clear() {fromList.clear();}@Overridepublic T get(int index) {return function.apply(fromList.get(index));}@Overridepublic Iterator<T> iterator() {return listIterator();}@Overridepublic ListIterator<T> listIterator(int index) {return new TransformedListIterator<F, T>(fromList.listIterator(index)) {@OverrideT transform(F from) {return function.apply(from);}};}@Overridepublic boolean isEmpty() {return fromList.isEmpty();}@Overridepublic T remove(int index) {return function.apply(fromList.remove(index));}@Overridepublic int size() {return fromList.size();}private static final long serialVersionUID = 0;}

源码的解释很清楚,Lists.transform返回的是一个新的类TransformingRandomAccessList,该类有两个变量

final List<F> fromList;
final Function<? super F, ? extends T> function;
也就是Lists.transform保存的只是原有的列表和向新列表转化的Function,每次遍历就重新计算一次。
@Override
public T get(int index) {return function.apply(fromList.get(index));
}
返回的列表是原有列表的一个转换视图,对原有集合的修改当然会反映到新集合中,这可以解释上述异常a。
由于functions不具有可逆性,transform是单向的,无法向结果列表中添加新元素,因此Lists.transform返回的l
ist不支持add和addAll方法。这可以解释异常c。
The returned list is a transformed view of fromList; changes to fromList will be reflected in the returned list
and vice versa.源码的注释表明对fromList的修改会反映到returnList上,对returnList的修改也会同样影响fromList,
这是不正确的,对returnList的修改不一定样影响fromList,没有必然的联系,这取决于Function对象中的转换方法,如
本测试方法用到的PersonDb向PersonVo转换方法personDbToVo,遍历returnList时每次都会调用personDbToVo,然后每次都会调用
PersonVo personVo = new PersonVo();生成新的对象,所以对结果列表returnList修改只会影响该局部变量personVo,而不会
影响到原来的fromList,这可以解释异常b。

public PersonVo personDbToVo(PersonDb personDb) {Preconditions.checkNotNull(personDb, "[PersonDbToVo]personDb为null");PersonVo personVo = new PersonVo();personVo.setName(personDb.getName() + ",from Db");personVo.setAge(personDb.getAge());personVo.setMsg(personDb.getMsg());return personVo;
}

3.问题避免

a.刚开始看Guava代码觉着Lists.transform是个好方法,很强大,但在使用的过程中发现其坑也是挺多的,不注意的话可能会
出现很严重的bug。所以考虑在只有在很必要的情况下才考虑用Lists.transform,即使用Lists.transform可以极大地减少代码量并
使得程序更清晰易懂。在使用复杂的开源类库前还是很有必要仔细阅读下源码的,在不清楚知道自己在干什么的时候最好还是
用成熟的解决方案去解决遇到的问题。
b.如果非要使用Lists.transform方法来实现集合转换,最好对returnList进行下后处理,如使用ImmutableList.copyOf和Lists.newArrayList
对返回结果进行下加工,这样就不用担心不可以对returnList结果进行必要修改了。但如果真的对returnList做上述处理,是否还真的有必要
调用Lists.transform?直接循环遍历过程中生成新的resultList是不是更好呢。

//personVos = ImmutableList.copyOf(personVos);
//personVos = Lists.newArrayList(personVos);//我认为直接循环遍历、转换生成resultList在时间和空间复杂度上会更好。


http://chatgpt.dhexx.cn/article/I62aOlNc.shtml

相关文章

Google guava工具类中Lists、Maps、Sets简单使用

Google guava Guava是对Java API的补充&#xff0c;对Java开发中常用功能进行更优雅的实现&#xff0c;使得编码更加轻松&#xff0c;代码容易理解。Guava使用了多种设计模式&#xff0c;同时经过了很多测试&#xff0c;得到了越来越多开发团队的青睐。Java最新版本的API采纳了…

Java常用工具类 : StringUtils、CollectionUtils、ArrayUtils、Lists、Maps等

文章目录 StringUtils引入依赖判断函数 (isNotBlank系列)大小写函数 (转换)删除函数 (remove)字符替换函数 (replace)拆分合并函数 (split)截取函数 (substring)删除空白函数 (trim)判断是否相等函数 (equals)是否包含函数 (contains) CollectionUtils集合判断函数并集、交集、…

Lists的使用

List&#xff08;接口&#xff09;顺序时List最重要的特性&#xff1a;它可保证元素按照规定的顺序排列。List为Collection添加了大量方法&#xff0c;以便我们在List中部插入和删除元素&#xff08;只推荐对LinkedList这样做&#xff09;。List也会生成一个ListIterator&#…

Lists 方法汇总

一、Lists.partition(List<T> list, int size) 将 list 集合按指定长度进行切分&#xff0c;返回新的List<List<T>>集合。 import com.google.common.collect.Lists; import org.junit.Test; import java.util.List;public class ListDemo {Testpublic voi…

LU分解的实现

LU分解是将矩阵分解为一个下三角矩阵和一个上三角矩阵的乘积。矩阵可以不是NxN的矩阵 一个可逆矩阵可以进行LU分解当且仅当它的所有子式都非零。如果要求其中的L矩阵&#xff08;或U矩阵&#xff09;为单位三角矩阵&#xff0c;那么分解是唯一的。同理可知&#xff0c;矩阵的L…

LU分解求线性方程组的解

LU分解是矩阵分解的一种&#xff0c;可以将一个矩阵分解为一个上三角矩阵和一个下三角矩阵的乘积。 LU分解可以用来求逆矩阵&#xff0c;解线性方程组等。本文将介绍LU分解求线性方程组的解。 1.定义 如果A是一个方阵&#xff0c;A的LU分解是将A分解成如下形式: 其中L,U分别为…

矩阵分析——LU分解

LU分解初步 矩阵的LU分解主要用来求解线性方程组或者计算行列式。在使用初等行变换法求解线性方程组的过程中&#xff0c;系数矩阵的变化情况如下&#xff1a; 由上可知&#xff1a; &#xff0c;其中U就是上面矩阵A经过行变换后的上三角矩阵&#xff0c;Eij表示将i行元素与j行…

Doolittle分解法(LU分解)详细分析以及matlab的实现

一、基本介绍 前面介绍的Gauss消去法实际上做的事情是将系数矩阵A做了一个三角分解&#xff0c;即&#xff1a; ALU 式&#xff08;1&#xff09; 其中&#xff0c;L为单位下三角阵&#xff0c;U为上三角阵&#xff0c;该分解唯一。若A为非奇异&#xff0c;则U也非奇异。…

线性代数笔记10——矩阵的LU分解

在线性代数中&#xff0c; LU分解(LU Decomposition)是矩阵分解的一种&#xff0c;可以将一个矩阵分解为一个单位下三角矩阵和一个上三角矩阵的乘积&#xff08;有时是它们和一个置换矩阵的乘积&#xff09;。LU分解主要应用在数值分析中&#xff0c;用来解线性方程、求反矩阵或…

线性代数——LU(LR)分解

文章目录 定义为什么要LU分解为什么能做到LU分解 利用LU分解求行列式值利用LU分解求解线性方程组利用LU分解求逆矩阵对角线上元素有0的情况 定义 定义&#xff1a;给定矩阵A&#xff0c;将A表示成下三角矩阵L和上三角矩阵U的乘积&#xff0c;称为LU分解。再进一步&#xff0c;…

LU分解Matlab算法分析

最近矩阵分析老师出了一道题目作为作业&#xff0c;是一道程序题&#xff0c;题目是对A矩阵做LU分解&#xff0c;要求能对A实现PALU的分解&#xff0c;并最终输出L&#xff0c;U&#xff0c;P矩阵。 先来解读下题目&#xff0c;寥寥几句话&#xff0c;里面囊括的信息量却不少&a…

LU分解,LDLT分解,Cholesky分解

LU分解 如果方阵是非奇异的&#xff0c;即的行列式不为0&#xff0c;LU分解总是存在的。 ALU&#xff0c;将系数矩阵A转变成等价的两个矩阵L和U的乘积&#xff0c;其中L和U分别是下三角和上三角矩阵&#xff0c;而且要求L的对角元素都是1&#xff0c;形式如下&#xff1a; 本…

矩阵的直接LU分解法

上篇博文由高斯消去法的矩阵形式推出了矩阵的LU分解&#xff1a;矩阵的三角分解法&#xff1b; 实际上&#xff0c;可以直接处理矩阵&#xff0c;得到矩阵的LU分解&#xff0c;这就是矩阵的直接LU分解&#xff1b;直接通过矩阵的元素得到计算LU元素的递推公式&#xff0c;不需…

LU分解、LDLT分解和Cholesky分解

LU分解 概念&#xff1a;假定我们能把矩阵A写成下列两个矩阵相乘的形式&#xff1a;ALU&#xff0c;其中L为下三角矩阵&#xff0c;U为上三角矩阵。这样我们可以把线性方程组Ax b写成 Ax (LU)x L(Ux) b。令Ux y&#xff0c;则原线性方程组Ax b可首先求解向量y 使Ly b&am…

A的LU分解

前面我们曾经通过高斯消元将矩阵A最终转化成了上三角阵U&#xff0c;那么今天我们就继续深入探索A和U之间到底有什么样的联系。在开始之前&#xff0c;先交代一些需用到的基础理论。假设A是可逆阵&#xff0c;则有AA-1I&#xff0c;两边同时转置&#xff0c;根据乘积的转置等于…

LU分解(matlab实现)

LU分解(LU Decomposition)是矩阵分解的一种&#xff0c;可以将一个矩阵分解为一个下三角矩阵和一个上三角矩阵的乘积。 主要的算法思路是从下至上地对矩阵A做初等行变换&#xff0c;将对角线左下方的元素变成零&#xff0c;这些行变换的效果等同于左乘一系列单位下三角矩阵&…

矩阵的LU分解,LU分解的推广,LU分解有什么意义,为什么要用LU分解。

一点点数学&#xff01;开干&#xff01; 参考书籍&#xff1a;《矩阵分析与计算》李继根 张新发编著 矩阵的LU分解&#xff1a; LU分解定理&#xff1a;如果n阶方阵A的各阶顺序主子式≠0&#xff08;K1、2、3&#xff0c;…&#xff0c;n&#xff09;&#xff0c;即A的各阶…

LU分解(图解)

三角分解(LU分解) 在线性代数中&#xff0c; LU分解(LU Decomposition)是矩阵分解的一种&#xff0c;可以将一个矩阵分解为一个单位下三角矩阵和一个上三角矩阵的乘积&#xff08;有时是它们和一个置换矩阵的乘积&#xff09;。LU分解主要应用在数值分析中&#xff0c;用来解线…

矩阵系列:LU分解

1矩阵LU分解模块 1.1 LU分解数学表达 首先要明确的是&#xff0c;矩阵的LU分解是有局限性的&#xff0c;即LU分解只针对非奇异矩阵。那么什么是非奇异矩阵呢&#xff1f;即各阶顺序主子式不为零。 &#xff08;1&#xff09;高斯消去法 LU分解的思想来源于高斯消去法&#xff…

LU分解(图解与应用举例)

三角分解(LU分解) 在线性代数中&#xff0c; LU分解(LU Decomposition)是矩阵分解的一种&#xff0c;可以将一个矩阵分解为一个单位下三角矩阵和一个上三角矩阵的乘积&#xff08;有时是它们和一个置换矩阵的乘积&#xff09;。LU分解主要应用在数值分析中&#xff0c;用来解线…