<commit_before>---
layout: post
title: Guava Collection Zero To Hero Part Two
---
第二部分主要展示Guava Collection的使用，从实际用例出发。

##Guava Collection Use Case
在了解了Guava的Function和Predicate后，可以开始使用了。这篇文章里面不是文档的堆积，而是一些具体的使用场景，这些场景仅仅是我遇到的，觉得在一定程度上简化了操作，美化了代码的，希望对读者有所启发，开发自己的Best Practice。

###测试用数据

测试使用一个自定义的数据结构，如下：
<pre><code class="Java">public class Node {
    private Integer id;
    private String title;
    private List&#60;Integer&#62; items;
}</code></code></pre>


###1. 使用transform做抽取

很多时候我们手中会拿到一组复杂的模型，不论是从数据库还是从别人的什么接口。但是真正在我们的逻辑中需要用到的仅仅是Model种的一个或多个字段。在这种情况下，你可以尝试Lists或者Iterables工具集中的transform方法来抽取我们需要的部分。位于Lists包内的函数签名为:
<pre><code class="Java">public static &#60;F, T&#62; List&#60;T&#62; transform(List&#60;F&#62; fromList, Function&#60; ? super F, ? extends T&#62; function);</code></pre>
传入的参数分别为待处理的数据List，以及一个实现具体抽取逻辑的Function。加入我们希望抽取出Node中的id，获得所有Node的id数组。那么我们可以定义如下的Function：传入一个Node，返回该Node的Id。
<pre><code class="Java">Function&#60;Node, Integer&#62; idExtractor = new Function&#60;Node, Integer&#62;() {
        public Integer apply(Node input) {
            return input.getId();
        }
};</code></pre>
然后使用transform即可：
<pre><code class="Java">List&#60;Node&#62; nodes = Lists.newArrayList(
    Node.nodeGenerator(1), Node.nodeGenerator(2), Node.nodeGenerator(3));
List&#60;Integer&#62; nodeIds = Lists.transform(nodes, idExtractor);</code></pre>

使用的语法是非常简单的，但是这背后有一些注意的事项，甚至是隐藏的陷阱。
####1.1 返回的仅仅是个Lazy的View
最容易犯的错误就是认为上面得到的结果 nodeIds是一个和我们在堆上分配的List一样的东西，但事实是，它不是的。更加准确的说，他甚至还不是他自己。为什么这么说呢？Lists是一个包含若干工具方法的类，可以理解为是博文Part One里面提到的组装场地，在这里数据和逻辑相互作用，具体的，transform方法会返回一个Guava自定义的List, 这种List并不会重新开辟内存来保存transform的返回值，它的内部仅仅保存了一个指向传入数据的引用，其get方法返回Function作用后的结果。当你用迭代器迭代这个List（包括foreach）的时候，会产生一个自定义的iterator：TransformedListIterator。这个iterator的next()返回被Function作用后的结果，迭代器禁用了set和add方法。他扩展了AbstractList，但并没有自己实现add和set方法，所以这两种操作也不被支持。总结一下：

+ 直接调用返回List的 add，set方法会抛出 <font color="red">UnsupportedOperationException</font>；
+ 视图直接操作返回List的Iterator来执行set，add，将会抛出<font color="red">UnsupportedOperationException</font>；
+ 直接调用返回List的remove或者用iterator来remove都会触发传入的原始数据的remove。所以如果传入的原始数据支持这个操作，那么你的操作就会成功。像上面传入的ArrayList，remove是支持的。所以transform的返回也是支持的。需要注意的是，你以为删除的是id，<font color="red">但是删除的实际是原始数据中的对象</font>；

另外需要注意的是，使用transform后你并没马上获得这个List，当你实际使用的时候才会生成，亦即lazy。如果你需要实际修改返回的List，那么需要显式的将结果拷贝出来。
<pre><code class="Java">List&#60;Integer&#62; nodeIds_supportModification = Lists.newArrayList(Lists.transform(nodes, idExtractor));</code></pre>

####1.2警惕你的异常被吞掉
在实现Function中的apply的时候，你或许会调用一个抛出checked异常的函数。但是你的IDE会给你一个错误，因为你Override的apply方法并没有抛出任何checked异常，为了规避这个问题，你可以把一个checked的异常转化为一个Runtime异常来抛出，从而让程序正常的编译，如下：

<pre><code class="Java">Function&#60;Node, Integer&#62; exceptionFunction = new Function&#60;Node, Integer&#62;() {
    public Integer apply(Node input) {
        try {
            input.strangeGet(); //throws a checked exception here
            return input.getId();
        } catch (MyException e) {
            throw Throwables.propagate(e);  //transfer to unchecked exception
        }
    }
};<commit_msg>guava part one and part two<commit_after>---
layout: post
title: Guava Collection Zero To Hero Part Two
---
第二部分主要展示Guava Collection的使用，从实际用例出发。

##Guava Collection Use Case
在了解了Guava的Function和Predicate后，可以开始使用了。这篇文章里面不是文档的堆积，而是一些具体的使用场景，这些场景仅仅是我遇到的，觉得在一定程度上简化了操作，美化了代码的，希望对读者有所启发，开发自己的Best Practice。

###测试用数据

测试使用一个自定义的数据结构，如下：
<pre><code class="Java">public class Node {
    private Integer id;
    private String title;
    private List&#60;Integer&#62; items;
}</code></code></pre>


###1. 使用transform做抽取

很多时候我们手中会拿到一组复杂的模型，不论是从数据库还是从别人的什么接口。但是真正在我们的逻辑中需要用到的仅仅是Model种的一个或多个字段。在这种情况下，你可以尝试Lists或者Iterables工具集中的transform方法来抽取我们需要的部分。位于Lists包内的函数签名为:
<pre><code class="Java">public static &#60;F, T&#62; List&#60;T&#62; transform(List&#60;F&#62; fromList, Function&#60; ? super F, ? extends T&#62; function);</code></pre>
传入的参数分别为待处理的数据List，以及一个实现具体抽取逻辑的Function。加入我们希望抽取出Node中的id，获得所有Node的id数组。那么我们可以定义如下的Function：传入一个Node，返回该Node的Id。
<pre><code class="Java">Function&#60;Node, Integer&#62; idExtractor = new Function&#60;Node, Integer&#62;() {
        public Integer apply(Node input) {
            return input.getId();
        }
};</code></pre>
然后使用transform即可：
<pre><code class="Java">List&#60;Node&#62; nodes = Lists.newArrayList(
    Node.nodeGenerator(1), Node.nodeGenerator(2), Node.nodeGenerator(3));
List&#60;Integer&#62; nodeIds = Lists.transform(nodes, idExtractor);</code></pre>

使用的语法是非常简单的，但是这背后有一些注意的事项，甚至是隐藏的陷阱。

<!--more-->

####1.1 返回的仅仅是个Lazy的View
最容易犯的错误就是认为上面得到的结果 nodeIds是一个和我们在堆上分配的List一样的东西，但事实是，它不是的。更加准确的说，他甚至还不是他自己。为什么这么说呢？Lists是一个包含若干工具方法的类，可以理解为是博文Part One里面提到的组装场地，在这里数据和逻辑相互作用，具体的，transform方法会返回一个Guava自定义的List, 这种List并不会重新开辟内存来保存transform的返回值，它的内部仅仅保存了一个指向传入数据的引用，其get方法返回Function作用后的结果。当你用迭代器迭代这个List（包括foreach）的时候，会产生一个自定义的iterator：TransformedListIterator。这个iterator的next()返回被Function作用后的结果，迭代器禁用了set和add方法。他扩展了AbstractList，但并没有自己实现add和set方法，所以这两种操作也不被支持。总结一下：

+ 直接调用返回List的 add，set方法会抛出 <font color="red">UnsupportedOperationException</font>；
+ 视图直接操作返回List的Iterator来执行set，add，将会抛出<font color="red">UnsupportedOperationException</font>；
+ 直接调用返回List的remove或者用iterator来remove都会触发传入的原始数据的remove。所以如果传入的原始数据支持这个操作，那么你的操作就会成功。像上面传入的ArrayList，remove是支持的。所以transform的返回也是支持的。需要注意的是，你以为删除的是id，<font color="red">但是删除的实际是原始数据中的对象</font>；

另外需要注意的是，使用transform后你并没马上获得这个List，当你实际使用的时候才会生成，亦即lazy。如果你需要实际修改返回的List，那么需要显式的将结果拷贝出来。
<pre><code class="Java">List&#60;Integer&#62; nodeIds_supportModification = Lists.newArrayList(Lists.transform(nodes, idExtractor));</code></pre>

####1.2警惕你的异常被吞掉
在实现Function中的apply的时候，你或许会调用一个抛出checked异常的函数。但是你的IDE会给你一个错误，因为你Override的apply方法并没有抛出任何checked异常，为了规避这个问题，你可以把一个checked的异常转化为一个Runtime异常来抛出，从而让程序正常的编译，如下：

<pre><code class="Java">Function&#60;Node, Integer&#62; exceptionFunction = new Function&#60;Node, Integer&#62;() {
    public Integer apply(Node input) {
        try {
            input.strangeGet(); //throws a checked exception here
            return input.getId();
        } catch (MyException e) {
            throw Throwables.propagate(e);  //transfer to unchecked exception
        }
    }
};
{'fraction_non_alphanumeric': 0.12610114192495922, 'fraction_numerical': 0.01827079934747145, 'mean_word_length': 8.534992223950233, 'pattern_counts': {'":': 0, '<': 62, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5009796', 'n_tokens_mistral': 3187, 'n_tokens_neox': 3418, 'n_words': 268}