来自Google的瓜娃简介之Lists


声明:本文转载自https://my.oschina.net/qixiaobo025/blog/1555377,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

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

本文发表于2017年10月24日 14:34
(c)注:本文转载自https://my.oschina.net/qixiaobo025/blog/1555377,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 3099 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1