来自Google的瓜娃简介之Lists


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集合 请特别注意

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值