关于LISt Process...
前2天听了熊老师的一个Session,关于List Process的,差点忘了总结,现记录如下:
从一个Story开始,现需求如下:(注:List中的每个元素都是都是整数)
(1)、给定一个List,把List中的每个元素+1,返回新的List;
(2)、给定一个List,把List中的每个元素*2,返回新的List;
(3)、给定一个List,取出其中的偶数,返回新的List;
以TDD的思维开发,先写Testcase
public class ListProcessTest { @Test public void test_every_element_in_list_add_one() throws Exception { List<Integer> inputList = Arrays.asList(1, 2, 3, 4); assertThat(ListProcess.addOne(inputList), is(Arrays.asList(2, 3, 4, 5))); } @Test public void test_every_element_in_list_multiply_two() throws Exception { List<Integer> inputList = Arrays.asList(1, 2, 3, 4); assertThat(ListProcess.multiplyTwo(inputList), is(Arrays.asList(2, 4, 6, 8))); } @Test public void test_get_even_from_input_list() throws Exception { List<Integer> inputList = Arrays.asList(1, 2, 3, 4); assertThat(ListProcess.getEvenList(inputList), is(Arrays.asList(2, 4))); }}第一次大家对addOne,multiplyTwo,getEvenList实现如下
public class ListProcess { public static List<Integer> addOne(List<Integer> inputList) { List<Integer> result = new ArrayList<Integer>(); for (int input : inputList) { result.add(input + 1); } return result; } public static List<Integer> multiplyTwo(List<Integer> inputList) { List<Integer> result = new ArrayList<Integer>(); for (int input : inputList) { result.add(input * 2); } return result; } public static List<Integer> getEvenList(List<Integer> inputList) { List<Integer> result = new ArrayList<Integer>(); for (int input : inputList) { if (input % 2 == 0) { result.add(input); } } return result; }}功能完成,测试通过之后,马上就回发现这段代码的Bad Smell,一共才5,6行的函数,居然和其他函数有4句代码是重复的,然后熊老师就开始给我们讲这个Session的主要目的。对于List的处理,我们通常可以分为3类
第一类:对List中的每个元素,做同样的处理,得到一个新的集合,即遍历列表,针对每个elment做一元函数Func(x)的映射,因为一一对应,因此可简称为Map操作;
第二类:判断List中的的每个元素是否满足某个条件,对满足条件的元素进行相应的逻辑处理得到新的List。比方说filter,select,reduce等等操作。
第三类:求取集合中所有元素的总值,比方说求和操作,这类操作对于每个元素来说都是一个2元操作,func(init_value,x),即把当前element的值加初始值上。可以简称为Accumulation操作。

在Google的collections包中就根据这样的思想提供了相应的工具类,改造过后的代码
public class ListProcess { public static List<Integer> addOne(List<Integer> inputList) { return Lists.transform(inputList, new Function<Integer, Integer>() { @Override public Integer apply(@Nullable Integer integer) { return ++integer; } }); } public static List<Integer> multiplyTwo(List<Integer> inputList) { return Lists.transform(inputList, new Function<Integer, Integer>() { @Override public Integer apply(@Nullable Integer integer) { return integer * 2; } }); } public static List<Integer> getEvenList(List<Integer> inputList) { //注:不知为何,Google Collections没有把Filter也做在Lists里面,而是放到Collections2里面,返回一个Collection<E>,我们不得不自己转成List List<Integer> result = new ArrayList<Integer>(); result.addAll( Collections2.filter(inputList, new Predicate<Integer>() { @Override public boolean apply(@Nullable Integer integer) { return integer % 2 == 0; } })); return result; }}这样改完之后有什么好处呢?第一:代码更直观,我们关注的只是function,predicate中的内容,也就是我们的核心业务逻辑。
第二:可以分布式处理,因为循环的时候CPU需要记录当前处理的元素下标之类的信号量,整个循环的处理过程会绑定在一个CPU上处理,就只能发挥一个CPU的计算能力,多核时代多浪费。如果使用Google的这种方式,就可以把一个很大的List拆分成一个个小的任务,然后并发执行之后再合并回来,也就是Map-Reduce的思想。
最后,熊老师推荐了一本经典书籍<Structure and Interpretation of Computer Programs>(计算机程序的构造和解释),说是看完之后,编程基本功会大大提高。刚加入清单,还没开始读,等读完之后再来写篇读后感。
补:关于为什么不把Filter的功能做到Lists类的原因参见:http://code.google.com/p/guava-libraries/wiki/IdeaGraveyard