Lists.partition是如何实现懒加载的?

前言:

最近看到一篇文章,里面提及了google的common包下Lists.partition方法为懒加载,只有在遍历时才会真正分区。平时使用时并未感觉到,感觉有点好奇。特此将自己寻找的答案的过程整理记录下来。

源码:

java 复制代码
 public static <T> List<List<T>> partition(List<T> list, int size) {
        Preconditions.checkNotNull(list);
        Preconditions.checkArgument(size > 0);
        return (List)(list instanceof RandomAccess ? new Lists.RandomAccessPartition(list, size) : new Lists.Partition(list, size));
    }
复制代码
判断了List的子类是否继承了RandomAccess接口,这个接口是个标记接口,实现它的实现类一般可以直接访问到具体的索引位置如ArrayList。像LinkedList虽然有索引,但是还是需要遍历全部找到位置的。此处的底层构造方法其实调用的是同一个。
内部类继承了AbstractList抽象类,并实现了List接口的get、size方法,重写了isEmpty方法。
java 复制代码
    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;
        }

        public List<T> get(int index) {
            Preconditions.checkElementIndex(index, this.size());
            int start = index * this.size;
            int end = Math.min(start + this.size, this.list.size());
            return this.list.subList(start, end);
        }

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

        public boolean isEmpty() {
            return this.list.isEmpty();
        }
    }

疑问一:内部类只重写了get方法,并未对此分片,它是如何做到的?

答案:AbstractList实现了iterator接口中方法(迭代器模式),迭代器中调用了get方法,实现了懒加载机制。

java 复制代码
   public boolean hasNext() {
//重写了size方法
            return cursor != size();
        }

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                //增强for的底层实际的就是迭代器,而next里面用到的get方法,
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }


//foreach的底层也是迭代器
 default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

疑问二:为什么不使用循环的时候,打印这个集合也会显示分区后的样子?

答案:toSting方法。AbstractList抽象类继承了AbstractCollection抽象类,而AbstractCollection重写了toString方法,AbstractList和Lists.partition均未重写该方法,说明调用就是AbstractCollection类的toString方法。而toString里面同样使用next方法。

java 复制代码
  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(' ');
        }
    }
复制代码
而println方法里的String.valueOf同样调用了打印目标toString方法。所以实现了懒加载。
java 复制代码
 public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

   public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

疑问三:没在代码里打印,debug的时候也可以看到分区成功?

网上查看了一下,ideaDebugger的设置,猜测同样有调用toString方法(此处并未验证,有兴趣的可以将这个去掉验证下)

如有其他答案,欢迎留言讨论。

相关推荐
BIG_PEI几秒前
检查并安装Redis
java
大貔貅喝啤酒2 分钟前
基于Windows下载安装Android Studio 3.3.2版本教程(2026详细图文版)
android·java·windows·android studio
奋斗的小方4 分钟前
Java基础篇09:项目实战
java·开发语言
海兰5 分钟前
【第21篇-续】graph-Stream-Node改造为适配openAI模型示例
java·人工智能·spring boot·spring·spring ai
vKd0Ff21L7 分钟前
如何在Dev-C++中设置TDM-GCC为默认编译器第九十一篇
java·jvm·c++
武子康9 分钟前
Java-221 RocketMQ 消息存储核心原理:CommitLog、ConsumerQueue、IndexFile 与消息过滤机制
java·大数据·分布式·消息队列·rabbitmq·rocketmq·java-rocketmq
北风toto17 分钟前
为什么 IntelliJ IDEA Community 无法开发 Vue?——附解决方案
java·vue.js·intellij-idea
programhelp_17 分钟前
Google 2026 New Grad SDE VO 三轮面试详解 | 含Behavioral、Coding、Design
java·服务器·数据库
驭渊的小故事22 分钟前
java中的进程的详细解析
java·开发语言
Mr_sst29 分钟前
Codex 部署、使用教程 & Vibe Coding 实战指南
java·ai·语言模型·chatgpt·ai编程