1.問題提出
1.前段時間在項目中用到Lists.transform返回的List,在對該list修改後發現修改並無反映在結果裏,研究源碼後發現問題還挺大。
下面經過單步調試的結果來查看Guava Lists.transform使用過程當中須要注意的地方。
a.對原有的list列表修改會影響Lists.transform已經生成列表java

由上圖能夠看出,對原數據集personDbs的修改會直接影響到Lists.transform方法返回的結果personVos,
這是很危險的,若是在使用的過程當中不注意的話會形成很嚴重的問題,而這種問題又是很隱蔽的,在項目中
無疑是個不定時的炸彈。
b.對Lists.transform生成的列表的元素進行修改可能沒法生效app

由上面的調試結果能夠看出對Lists.transform返回的List列表中的元素的修改不會"生效",即修改不會反映在list列表中。
c.對returnList調用add、addAll和shuffle等修改returnList的方法會拋異常
對personVos調用Collections.shuffle(personVos);或personVos.add(personDbToVo(new PersonDb("sting", 30)));
都會拋出java.lang.UnsupportedOperationException。
附測試代碼:dom
- package com.google.common.base;
-
- import com.google.common.collect.Lists;
- import org.junit.Test;
-
- import java.util.List;
-
- 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;
- }
-
- @Test
- public 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>() {
- @Override
- public PersonVo apply(PersonDb personDb) {
- return personDbToVo(personDb);
- }
- });
- for(PersonDb personDb : personDbs) {
- personDb.setMsg("hello world!");
- }
-
-
-
- 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;
- }
- @Override
- public 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;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("name", name)
- .add("age", age)
- .add("msg", msg).toString();
- }
- }
2.源碼解讀和異常分析
帶着上面的三個問題去查看源碼
-
- @CheckReturnValue
- public 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);
- }
-
- @Override
- public void clear() {
- fromList.clear();
- }
-
- @Override
- public T get(int index) {
- return function.apply(fromList.get(index));
- }
-
- @Override
- public Iterator<T> iterator() {
- return listIterator();
- }
-
- @Override
- public ListIterator<T> listIterator(int index) {
- return new TransformedListIterator<F, T>(fromList.listIterator(index)) {
- @Override
- T transform(F from) {
- return function.apply(from);
- }
- };
- }
-
- @Override
- public boolean isEmpty() {
- return fromList.isEmpty();
- }
-
- @Override
- public T remove(int index) {
- return function.apply(fromList.remove(index));
- }
-
- @Override
- public int size() {
- return fromList.size();
- }
-
- private static final long serialVersionUID = 0;
- }
源碼的解釋很清楚,Lists.transform返回的是一個新的類TransformingRandomAccessList,該類有兩個變量ide
- 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是否是更好呢。