title: 来自Google的瓜娃简介之Lists tags:
- Guava
- Lists
- transform
- 函数式
- RandomAccess categories: guava date: 2017-10-24 16:10:59
注:以下分析均基于Guava18
背景
在很久很久以前(大概就是jdk7之前吧)没有钻石语法的我们苦逼的一遍又一遍的写着泛型
List<String> list=new ArrayList<String>();
复制代码
一直饱受摧残,直到发现了Guava,我们直接使用API创建
List<String> list=Lists.newArrayList();
复制代码
不需要重复两遍的感觉不错,当然现在有了钻石语法现在可以
List<String> list=new ArrayList<>();
复制代码
进阶
newArrayList(E... elements)
我们经常需要一个一次性的方法来调用,需要传入几个元素到List中
Lists 提供了一个静态方法来做初始化
Lists.newArrayList("a","b","c");
复制代码
这个比较快捷方便
newArrayListWithCapacity(int initialArraySize)
我们直接调用ArrayList的构造函数其实是对容量的设置,jdk版本定义都比较模糊
从前一篇文章中我们知道最熟悉的ArrayList 其实默认容量为10 每次double size
容量对于arrayList的性能至关重要,事实上同样的数据丢入到ArrayList中
由于容量的不同造成的结果可能性能相差数十倍
一般来说使用该方法的频率仍然比较小
Lists.newArrayListWithCapacity(10);
复制代码
newArrayListWithExpectedSize(int estimatedSize)
该方法一般来说是Guava里面使用最常见的系列newXXXWithExpectedSize(int size)
我们通过该方法可以设置一个期望的大小,而不是容量。对于ArrayList来说容量和阈值可以等同
而在hashMap等集合中阈值和容量时两个不同的概念。Guava会自动帮我们计算出合适的容量
传给对应的构造函数
/**
* Creates an {@code ArrayList} instance to hold {@code estimatedSize}
* elements, <i>plus</i> an unspecified amount of padding; you almost
* certainly mean to call {@link #newArrayListWithCapacity} (see that method
* for further advice on usage).
*
* <p><b>Note:</b> This method will soon be deprecated. Even in the rare case
* that you do want some amount of padding, it's best if you choose your
* desired amount explicitly.
*
* @param estimatedSize an estimate of the eventual {@link List#size()} of
* the new list
* @return a new, empty {@code ArrayList}, sized appropriately to hold the
* estimated number of elements
* @throws IllegalArgumentException if {@code estimatedSize} is negative
*/
@GwtCompatible(serializable = true)
public static <E> ArrayList<E> newArrayListWithExpectedSize(
int estimatedSize) {
return new ArrayList<E>(computeArrayListCapacity(estimatedSize));
}
@VisibleForTesting static int computeArrayListCapacity(int arraySize) {
checkNonnegative(arraySize, "arraySize");
// TODO(kevinb): Figure out the right behavior, and document it
return Ints.saturatedCast(5L + arraySize + (arraySize / 10));
}
复制代码
我们通常这样调用
Lists.newArrayListWithExpectedSize(10);
复制代码
除了一些常见的newXXX等方法 Lists还提供了其他的常用方法
复制代码
典型的包括OnePlusArrayList TwoPlusArrayList等
cartesianProduct
使用笛卡尔积想来也是比较常见的需求。开发者收到多个List求对应笛卡尔积
/**
* Returns every possible list that can be formed by choosing one element
* from each of the given lists in order; the "n-ary
* <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian
* product</a>" of the lists. For example: <pre> {@code
*
* Lists.cartesianProduct(ImmutableList.of(
* ImmutableList.of(1, 2),
* ImmutableList.of("A", "B", "C")))}</pre>
*
* <p>returns a list containing six lists in the following order:
*
* <ul>
* <li>{@code ImmutableList.of(1, "A")}
* <li>{@code ImmutableList.of(1, "B")}
* <li>{@code ImmutableList.of(1, "C")}
* <li>{@code ImmutableList.of(2, "A")}
* <li>{@code ImmutableList.of(2, "B")}
* <li>{@code ImmutableList.of(2, "C")}
* </ul>
*
* <p>The result is guaranteed to be in the "traditional", lexicographical
* order for Cartesian products that you would get from nesting for loops:
* <pre> {@code
*
* for (B b0 : lists.get(0)) {
* for (B b1 : lists.get(1)) {
* ...
* ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...);
* // operate on tuple
* }
* }}</pre>
*
* <p>Note that if any input list is empty, the Cartesian product will also be
* empty. If no lists at all are provided (an empty list), the resulting
* Cartesian product has one element, an empty list (counter-intuitive, but
* mathematically consistent).
*
* <p><i>Performance notes:</i> while the cartesian product of lists of size
* {@code m, n, p} is a list of size {@code m x n x p}, its actual memory
* consumption is much smaller. When the cartesian product is constructed, the
* input lists are merely copied. Only as the resulting list is iterated are
* the individual lists created, and these are not retained after iteration.
*
* @param lists the lists to choose elements from, in the order that
* the elements chosen from those lists should appear in the resulting
* lists
* @param <B> any common base class shared by all axes (often just {@link
* Object})
* @return the Cartesian product, as an immutable list containing immutable
* lists
* @throws IllegalArgumentException if the size of the cartesian product would
* be greater than {@link Integer#MAX_VALUE}
* @throws NullPointerException if {@code lists}, any one of the {@code lists},
* or any element of a provided list is null
*/ static <B> List<List<B>>
cartesianProduct(List<? extends List<? extends B>> lists) {
return CartesianList.create(lists);
}
复制代码
注释已经写得比较详细了
transform
在jdk8没有使用之前使用Guava的函数式编程也是一个不错的选择。
特别配合IDEA会将对应代码缩进和lambada类似的效果
开发者经常碰到如下的需求 将已经有的list转换为另一个list 比如加工某个字段,比如提取某个字段
那么就是transform大显身手的时候了
/**
* 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.
*/
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);
}
复制代码
transform 根据传进来的list是否支持随机访问区分了不同的实现类
相比较我们常规的实现可能就是遍历一个list然后取得元素加工后放入
新的容器中
但是Google并没有如此实现 取而代之抽象了加工元素的方法定义为Function
因此我们需要传入进去一个Function
/**
* Determines an output value based on an input value.
*
* <p>The {@link Functions} class provides common functions and related utilites.
*
* <p>See the Guava User Guide article on <a href=
* "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use of {@code
* Function}</a>.
*
* @author Kevin Bourrillion
* @since 2.0 (imported from Google Collections Library)
*/
@GwtCompatible
public interface Function<F, T> {
/**
* Returns the result of applying this function to {@code input}. This method is <i>generally
* expected</i>, but not absolutely required, to have the following properties:
*
* <ul>
* <li>Its execution does not cause any observable side effects.
* <li>The computation is <i>consistent with equals</i>; that is, {@link Objects#equal
* Objects.equal}{@code (a, b)} implies that {@code Objects.equal(function.apply(a),
* function.apply(b))}.
* </ul>
*
* @throws NullPointerException if {@code input} is null and this function does not accept null
* arguments
*/
@Nullable T apply(@Nullable F input);
/**
* Indicates whether another object is equal to this function.
*
* <p>Most implementations will have no reason to override the behavior of {@link Object#equals}.
* However, an implementation may also choose to return {@code true} whenever {@code object} is a
* {@link Function} that it considers <i>interchangeable</i> with this one. "Interchangeable"
* <i>typically</i> means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for all
* {@code f} of type {@code F}. Note that a {@code false} result from this method does not imply
* that the functions are known <i>not</i> to be interchangeable.
*/
@Override
boolean equals(@Nullable Object object);
}
复制代码
我们仅需要实现一个apply的接口
我们可以看一下RandomAccess的区别
/**
* Implementation of a transforming random access list. We try to make as many
* of these methods pass-through to the source list as possible so that the
* performance characteristics of the source list and transformed list are
* similar.
*
* @see Lists#transform
*/
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;
}
/**
* Implementation of a sequential transforming list.
*
* @see Lists#transform
*/
private static class TransformingSequentialList<F, T>
extends AbstractSequentialList<T> implements Serializable {
final List<F> fromList;
final Function<? super F, ? extends T> function;
TransformingSequentialList(
List<F> fromList, Function<? super F, ? extends T> function) {
this.fromList = checkNotNull(fromList);
this.function = checkNotNull(function);
}
/**
* The default implementation inherited is based on iteration and removal of
* each element which can be overkill. That's why we forward this call
* directly to the backing list.
*/
@Override public void clear() {
fromList.clear();
}
@Override public int size() {
return fromList.size();
}
@Override public ListIterator<T> listIterator(final int index) {
return new TransformedListIterator<F, T>(fromList.listIterator(index)) {
@Override
T transform(F from) {
return function.apply(from);
}
};
}
private static final long serialVersionUID = 0;
}
复制代码
很明显使用ArrayList的性能比较高,同时Guava的Lists提供出来的方法不会瞬间生成太多的对象,性能比较好。其实这个生成的List是一个映射List 因此对于产生的List做的操作会对原List产生影响。
举个实际的例子
List<TmServiceInfoDetail> details = Lists.transform(Lists.newArrayList(idOwnOrgs), new Function<String, TmServiceInfoDetail>() {
@Override
public TmServiceInfoDetail apply(String input) {
TmServiceInfoDetail infoDetail = DozerHelper.clone(detail);
infoDetail.setIdOwnOrg(input);
return infoDetail;
}
});
复制代码
我们在IDEA上看到的结果这样
partition
另一个实际的例子我们获得List需要按照尺寸切分 比如100的size 我们需要切分成30 30 30 10
那么partition是您的不二之选
/**
* Returns consecutive {@linkplain List#subList(int, int) sublists} of a list,
* each of the same size (the final list may be smaller). For example,
* partitioning a list containing {@code [a, b, c, d, e]} with a partition
* size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing
* two inner lists of three and two elements, all in the original order.
*
* <p>The outer list is unmodifiable, but reflects the latest state of the
* source list. The inner lists are sublist views of the original list,
* produced on demand using {@link List#subList(int, int)}, and are subject
* to all the usual caveats about modification as explained in that API.
*
* @param list the list to return consecutive sublists of
* @param size the desired size of each sublist (the last may be
* smaller)
* @return a list of consecutive sublists
* @throws IllegalArgumentException if {@code partitionSize} is nonpositive
*/
public static <T> List<List<T>> partition(List<T> list, int size) {
checkNotNull(list);
checkArgument(size > 0);
return (list instanceof RandomAccess)
? new RandomAccessPartition<T>(list, size)
: new Partition<T>(list, size);
}
private static class Partition<T> extends AbstractList<List<T>> {
final List<T> list;
final int size;
Partition(List<T> list, int size) {
this.list = list;
this.size = size;
}
@Override public List<T> get(int index) {
checkElementIndex(index, size());
int start = index * size;
int end = Math.min(start + size, list.size());
return list.subList(start, end);
}
@Override public int size() {
return IntMath.divide(list.size(), size, RoundingMode.CEILING);
}
@Override public boolean isEmpty() {
return list.isEmpty();
}
}
private static class RandomAccessPartition<T> extends Partition<T>
implements RandomAccess {
RandomAccessPartition(List<T> list, int size) {
super(list, size);
}
}
复制代码
比如实际的例子
@Around("listReturnMethod(batch)&&listArgumentMethod(list)")
public Object batchExecuteReturnList(ProceedingJoinPoint joinPoint, Batch batch, List list) throws Throwable {
Stopwatch started = Stopwatch.createStarted();
int batchSize = batch.value();
List rltList = Lists.newArrayListWithExpectedSize(list.size());
List<List> partitions = Lists.partition(list, batchSize);
for (List partition : partitions) {
Object rlt = joinPoint.proceed(new Object[]{partition});
rltList.addAll((Collection) rlt);
}
logger.info(joinPoint.getSignature().getName()+" size:"+list.size()+" cost:"+String.valueOf(started.elapsed(TimeUnit.MILLISECONDS)+"ms"));
return rltList;
}
复制代码
reverse
反序list也是我们的常见需求
/**
* Returns a reversed view of the specified list. For example, {@code
* Lists.reverse(Arrays.asList(1, 2, 3))} returns a list containing {@code 3,
* 2, 1}. The returned list is backed by this list, so changes in the returned
* list are reflected in this list, and vice-versa. The returned list supports
* all of the optional list operations supported by this list.
*
* <p>The returned list is random-access if the specified list is random
* access.
*
* @since 7.0
*/
public static <T> List<T> reverse(List<T> list) {
if (list instanceof ImmutableList) {
return ((ImmutableList<T>) list).reverse();
} else if (list instanceof ReverseList) {
return ((ReverseList<T>) list).getForwardList();
} else if (list instanceof RandomAccess) {
return new RandomAccessReverseList<T>(list);
} else {
return new ReverseList<T>(list);
}
}
复制代码
同样的道理Google在实现时均是使用的老的结合作为数据提供 因此以上的方法都可以认为操作新的数据会影响到source集合 请特别注意