注:以下分析均基于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集合 请特别注意