Guava中Lists.partition(List, size) 方法懒划分/懒分区

背景

前几天有同事使用这个方法,不小心点进去查看源码,源码如下,然他通过idea工具debug发现执行完Lists.partition(List, size) 这一行直接就现实了个size大小如下图:看了源码后根本就没有显示的调用size这些啊,就在那思考不知道为什么?

Lists.partition(List, size)源码如下:

@GwtCompatible(emulated = true)
public final class Lists {
.....
public static <T> List<List<T>> partition(List<T> list, int size) {
checkNotNull(list);
checkArgument(size > 0);
return (list instanceof RandomAccess)
? new RandomAccessPartition<>(list, size)
: new Partition<>(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);
}
}
.....
}

分析

发现了问题,我们就应该分析为什么会出现这情况?

通过上面贴出来的代码以及Guava的源码,发现就是创建了一个Partition类过后啥都没做。为什么?

显示为什么调用toString?

通过查看idea--setting--Debugger--java可以看到。

那我们跟进代码后发现在AbstractCollection类中重写了toString方法,代码如下:

    public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]"; StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}

刚才不是说会调用toString方法么?那问题来了应该显示一个数组,不应该显示size ?

应该显示一个数组,不应该显示size

在上面的截图中我们可以看到复选框勾选了好几个,其中有个Enable alternative view for Collections classes.好现在我们将这个去掉。看看执行的结果如下图:

通过结果,我们发现现在显示的是数组了。看来Enable alternative view for Collections classes这个选项是专门针对集合来显示的。

如何是懒划分/懒分区

懒划分/懒分区意思就是当我们真正使用的时候才会去划分。下面分析下如何来懒划分的。验证demo如下:

public class Test {
public static void main(String[] args) {
List<String> alist= Lists.newArrayList("12","a","34");
List<List<String>> sList = Lists.partition(alist, 2);
sList.forEach(e -> {
System.out.println(sList);
});
}
}

我们看下forEach的源码如下:

public interface Iterable<T> {
....
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
.... }

看到这里是否有点懵逼。这里套了一层循环,没有显示调用分区呢?这个需要对for循环深入研究才行。我们可以通过简单写一个for循环后查看字节码看看底层到底是如何实现循环的。简单的for循环如下:

public class Test2 {
public static void main(String[] args) {
List<Integer> il = Lists.newArrayList(12,1,3,4);
for (Integer i : il) {}
}
}

通过javap -c Test2.class反解析字节码后如下图:

从图中这些关键字信息

38: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;

45: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z

54: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;

这说明了我们for循环底层上还是通过迭代器来实现的。我们也可以看出依次调用iterator()->hasNext()->next()

从开篇源码看出Guava中Partition的继承关系,发现当底层循环的时候调用iterator()方法是其实是调用AbstractList#iterator()方法,AbstractList#iterator()涉及源码如下

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
public Iterator<E> iterator() {
return new Itr();
}
...
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}

通过上面的源码,发现当调用hasNext()方法的时候会调用size()方法。然size()方法就是我们Guava中这段代码

public int size() {
return IntMath.divide(list.size(), size, RoundingMode.CEILING);
}

当调用next()方法的时候,先是调用子类实现的get(i)方法,子类也就是Guava中这段代码

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);
}

到此已基本分析完成了。

总结

我们在学习任何知识点的时候都要好好分析,想想,为什么要这样设计?设计的好处是什么?还有就是知识的串联。

比如for 循环。如果不深入研究,你也不知道为啥会调用iterator等一系列的方法。

Guava中Lists.partition(List, size) 方法懒划分/懒分区的更多相关文章

  1. Guava中Predicate的常见用法

    Guava中Predicate的常见用法 1.  Predicate基本用法 guava提供了许多利用Functions和Predicates来操作Collections的工具,一般在 Iterabl ...

  2. ConcurrentHashMap的size方法是线程安全的吗?

    前言 之前在面试的过程中有被问到,ConcurrentHashMap的size方法是线程安全的吗? 这个问题,确实没有答好.这次来根据源码来了解一下,具体是怎么一个实现过程. ConcurrentHa ...

  3. ConcurrentHashmap中的size()方法简单解释

    本文所有的源码都是基于JDK1.8 ConcurrentHashmap中的size()方法源码: public int size() { long n = sumCount(); return ((n ...

  4. 使用size()方法输出列表中的元素数量。需要注意的是,这个方法返回的值可能不是真实的,尤其当有线程在添加数据或者移除数据时,这个方法需要遍历整个列表来计算元素数量,而遍历过的数据可能已经改变。仅当没有任何线程修改列表时,才能保证返回的结果是准确的。

    使用size()方法输出列表中的元素数量.需要注意的是,这个方法返回的值可能不是真实的,尤其当有线程在添加数据或者移除数据时,这个方法需要遍历整个列表来计算元素数量,而遍历过的数据可能已经改变.仅当没 ...

  5. Google Guava中的前置条件

    前置条件:让方法调用的前置条件判断更简单. Guava在Preconditions类中提供了若干前置条件判断的实用方法,我们建议[在Eclipse中静态导入这些方法]每个方法都有三个变种: check ...

  6. 使用Lists.partition切分性能优化

    项目实战 影拓邦电影同步中,使用Lists.partition按500条长度进行切分,来实现es的同步. 切分的List为 使用介绍及示例 将list集合按指定长度进行切分,返回新的List<L ...

  7. Guava中这些Map的骚操作,让我的代码量减少了50%

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合.缓存.并发.I/O等多个方面.使用这些API一方面 ...

  8. 解析Jquery取得iframe中元素的几种方法

    iframe在复合文档中经常用到,利用jquery操作iframe可以大幅提高效率,这里收集一些基本操作,需要的朋友可以参考下   DOM方法:父窗口操作IFRAME:window.frames[&q ...

  9. Java小知识--length,length(),size()方法详细介绍

    Java中length,length(),size()区别 length属性:用于获取数组长度. eg: int ar[] = new int{1,2,3} /** * 数组用length属性取得长度 ...

随机推荐

  1. faceswap深度学习AI实现视频换脸详解

    给大家介绍最近超级火的黑科技应用deepfake,这是一个实现图片和视频换脸的app.前段时间神奇女侠加尔盖朵的脸被换到了爱情动作片上,233333.我们这里将会从github项目faceswap开始 ...

  2. 二元变量图形的pandas方法

    数据加载: 1.散点图 上图使用下采样的方法选取了100个样本点,因为把所有的数据加载进来太多了. 2.Hexplot图 上图是一个散点图再加上热力标注的形式,可以更准确的帮助我们看出数据集中在哪些区 ...

  3. java实现判定新旧版本号

    废话不多说,直接上代码 /** * 判断是否为最新版本方法 将版本号根据.切分为int数组 比较 * * @param localVersion 本地版本号 * @param onlineVersio ...

  4. Linux安装配置go运行环境

    1. 下载go,解压 gz包 wget https://storage.googleapis.com/golang/go1.7.5.linux-amd64.tar.gz tar zxvf go1.7. ...

  5. 8.SpringMVC注解式开发-HelloWorld

    第一个注解式开发程序 SpringMVC 是 Spring 框架的一部分,所以它和Spring结合的是非常紧密的 使用 @Controller 注解,既可以将处理器交给Spring容器去管理,又可以说 ...

  6. JAVA 内存模型(主内存,工作内存)

    JVM将内存组织为主内存和工作内存两个部分. 主内存是所有的线程所共享的,主要包括本地方法区和堆. 每个线程都有一个工作内存不是共享的,工作内存中主要包括两个部分: 1:一个是属于该线程私有的栈; 2 ...

  7. 【坑】Java中遍历递归删除List元素

    运行环境 idea 2017.1.1 需求背景 需要做一个后台,可以编辑资源列表用于权限管理 资源列表中可以有父子关系,假设根节点为0,以下以(父节点id,子节点id)表示 当编辑某个资源时,需要带出 ...

  8. redis系列二: linux下安装redis

    下面介绍在Linux环境下,Redis的安装与配置 一. 安装 1.首先上官网下载Redis 压缩包,地址:http://redis.io/download 下载稳定版3.0即可. 2.通过远程管理工 ...

  9. python 之禅 又名 蛇宗三字经

    打开cmd 输入python回车 import this Beautiful is better than ugly. Explicit is better than implicit. Simple ...

  10. Hadoop_19_MapReduce&&Yarn运行机制

    1.YARN的运行机制 1.1.概述: Yarn集群:负责海量数据运算时的资源调度,集群中的角色主要有:ResourceManager.NodeManager Yarn是一个资源调度(作业调度和集群资 ...