java解锁_Java 姿势解锁 —— Lists.transform

3a2b22fa92b729d1c0eff4197c013b8e.png

这个用法最初是在工作中读浩哥代码看到的,由于当时时间紧张,粗略看了一下以为就是个生成 List 的方法,于我而言其最多使用的地方在 Entity、Dto、From 间的互转,以至于在今后所有的涉及到转换的地方我都开始倾向于使用此方法,但是突然有一次就掉坑了…

先看看一段正常的代码1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63public class{

public static void main(String[] args){

List userList = Lists.newArrayList(new User("Alex", 12, "Play"), new User("August", 18, "Play"), new User("Ada", 11, "Play"));

List userFormList = new ArrayList(userList.size());

userFormList = Lists.transform(userList, new Function(){

public UserForm apply(User input){

UserForm form = new UserForm();

form.setName(input.getName());

form.setAge(input.getAge());

form.setHobby(input.getHobby());

return form;

}

});

for (UserForm form : userFormList){

System.out.println(form);

}

}

}

@Getter

@Setter

class User{

String name;

Integer age;

String hobby;

public User(){

}

public User(String name, Integer age, String hobby){

super();

this.name = name;

this.age = age;

this.hobby = hobby;

}

public String toString(){

return "User [name=" + name + ", age=" + age + ", hobby=" + hobby + "]";

}

}

@Getter

@Setter

class UserForm{

String name;

Integer age;

String hobby;

Integer id;

public String toString(){

return "UserForm [name=" + name + ", age=" + age + ", hobby=" + hobby + ", Id=" + id +"]";

}

}

输出结果:1

2

3UserForm [name=Alex, age=12, hobby=Play, Id=null]

UserForm [name=August, age=18, hobby=Play, Id=null]

UserForm [name=Ada, age=11, hobby=Play, Id=null]

这段代码作用就是快速通过 List 生成 List,没什么毛病对吧,接下来我们想在生成的 List 中修改 hobby 和 Id,看看会发生什么:1

2

3

4

5

6

7

8

9Random random = new Random();

for (UserForm form : userFormList){

form.setHobby("Study, It makes me happy~");

form.setId(String.valueOf(random.nextInt(20)));

System.out.println(form);

}

for (UserForm form : userFormList){

System.out.println(form);

}

输出结果:1

2

3

4

5

6

7UserForm [name=Alex, age=12, hobby=Study, It makes me happy~, Id=15]

UserForm [name=August, age=18, hobby=Study, It makes me happy~, Id=17]

UserForm [name=Ada, age=11, hobby=Study, It makes me happy~, Id=2]

UserForm [name=Alex, age=12, hobby=Play, Id=null]

UserForm [name=August, age=18, hobby=Play, Id=null]

UserForm [name=Ada, age=11, hobby=Play, Id=null]

咦! 第一次迭代的时候输出结果和我们预期一样,但是第二次的结果怎么显示成这个样子??

这么神奇的吗??

56f7618660b7a86ab410d8867ae6888d.png

Debug 分析问题

带着疑惑,我们 Debug 走一下流程

首先在执行 Lists.transform() 的时候会 先判断是否是 RandomAccess 类型,结合 RandomAccess 展开树我们可以发现,ArrayList 实现了 RandomAccess 接口

6638b04fb1ebfab3faa528cd43782ca7.png

91b3f6533d31a693d51c9daa9e3c89ef.png

紧接着会创建 TransformingRandomAccessList 类,并赋值 fromList 和 function

7d1f7965c2d9b55f620993b9804382e3.png

创建完成之后,function 是没有立即执行的,此时代码直接跳转到了 迭代 userFormList 的地方

28490448d3edfac06fc91647b9dee239.png

到此时开始执行 TransformingRandomAccessList 中的 listIterator() 方法开始迭代生成数据,这时候每次都会执行 transform() 方法,传入 F(From) 来源 User 类型数据,生成 T(Traget) 目标 UserForm 类型数据

6f1944916f70702ae21c1183235abaf0.png

73ff9a5c041f300e2145280697f0b41c.png

说说我的理解:我们的 userFormList 在执行 Lists.transform 后是没有立刻被赋值的,它只是被转换成了 TransformingRandomAccessList 类型,此类型 重写了迭代器,每次当我们迭代 userFormList 时候,它才会调用 function 进行赋值。

带着上面的理解我们来重新看一次这段代码:1

2

3

4

5

6

7

8

9Random random = new Random();

for (UserForm form : userFormList){

form.setHobby("Study, It makes me happy~");

form.setId(String.valueOf(random.nextInt(20)));

System.out.println(form);

}

for (UserForm form : userFormList){

System.out.println(form);

}

userFormList 在第一次迭代的时候会执行 Function 中的 UserForm form = new UserForm(); 此时会创建新的 UserForm,然后进行 set 不同的值,在我们第二次进行 迭代的时候,此时又会执行 Function 中的 UserForm form = new UserForm(); 这时候会再次创建 UserForm,当你 Debug 的时候会发现两次创建的 UserForm 地址是不一样的,这样就能解释为什么我们 set 的时候不起作用啦~

带着上面得出的结论来看看源码中的官方文档:

52a3077107e56b5588cff22c7480098b.png

可以说官方文档已经安排得明明白白的,它提到 Lists.transform 返回的每一个元素是 function 执行后的结果,其返回的 list 就是一个转换后的 “视图”,修改 原有的 list 会影响到现有的 list。并且 function 方法是不可逆的,生成的 list 不支持 add、addAll、set 方法,这个 function 是类似一种懒加载的机制,只有在需要的时候被调用,如果你想让返回的 List 不受此限制的话,需要自己动手 copy…

最后它还提了一句,此方法返回的 list 在你进行序列化的时候可能出现一些奇怪的操作,所以不推荐序列化。

突破 set 无法成功

在了解这个方法之后,我发现这个方法限制还蛮多的,于是找到浩哥,问了一下此方法用意,原以为是有性能上的优化,结果发现只是为了代码简洁一点,性能对比还没有普通写法来得快…

b7322ed77e9b6787c18598bd42bf5b8f.png

之前我们发现需要 set 的时候不能再次迭代 List,所以只有写在 Function 中才能完成我们的迭代咯,于是我又模拟了一下最常见的情况,我们稍微修改一下现有的代码:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65@Getter

@Setter

class User{

String name;

Integer age;

String hobby;

Integer id;

public User(){

}

public User(String name, Integer age, String hobby, Integer id){

super();

this.name = name;

this.age = age;

this.hobby = hobby;

this.id = id;

}

public String toString(){

return "User [name=" + name + ", age=" + age + ", hobby=" + hobby + ", id=" + id + "]";

}

}

@Getter

@Setter

class UserForm{

String name;

Integer age;

String hobby;

String secret;

public String toString(){

return "UserForm [name=" + name + ", age=" + age + ", hobby=" + hobby + ", Id=" + secret + "]";

}

}

@Test

public void ttest(){

List userList = Lists.newArrayList(new User("Alex", 12, "Play", 1), new User("August", 18, "Play", 2), new User("Ada", 11, "Play", 3));

Map secretMap = new HashMap();

secretMap.put(1, "biubiubiu~");

secretMap.put(2, "miaomiaomiao~");

secretMap.put(3, "qiuqiuqiu~");

List userFormList = new ArrayList(userList.size());

userFormList = Lists.transform(userList, new Function(){

@Override

public UserForm apply(User user){

UserForm form = new UserForm();

form.setName(user.getName());

form.setAge(user.getAge());

form.setHobby(user.getHobby());

form.setSecret(secretMap.get(user.getId()));

return form;

}

});

}

注意这里: form.setSecret(secretMap.get(user.getId())); 这里编译的时候会报错:

Cannot refer to non-final local variable secretMap define in an enclosing scope.

提示 secretMap 不是 final 修饰的变量,难道还需要使用 final 类型的变量吗? (至于为什么需要 final 修饰的变量,这个问题后面有时间再研究咯~)

将上面的代码改一下就能生效啦:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29@Test

public void ttest(){

List userList = Lists.newArrayList(new User("Alex", 12, "Play", 1), new User("August", 18, "Play", 2), new User("Ada", 11, "Play", 3));

Map secretMap = new HashMap();

secretMap.put(1, "biubiubiu~");

secretMap.put(2, "miaomiaomiao~");

secretMap.put(3, "qiuqiuqiu~");

final Map finalSecretMap = secretMap;

List userFormList = new ArrayList(userList.size());

userFormList = Lists.transform(userList, new Function(){

@Override

public UserForm apply(User user){

UserForm form = new UserForm();

form.setName(user.getName());

form.setAge(user.getAge());

form.setHobby(user.getHobby());

form.setSecret(finalSecretMap.get(user.getId()));

return form;

}

});

for (UserForm form : userFormList){

System.out.println(form);

}

}

再看看输出结果:1

2

3UserForm [name=Alex, age=12, hobby=Play, Id=biubiubiu~]

UserForm [name=August, age=18, hobby=Play, Id=miaomiaomiao~]

UserForm [name=Ada, age=11, hobby=Play, Id=qiuqiuqiu~]

Nice~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值