不使用size()方法实现LinkedList的Spliterator按规则拆分
解决LinkedList的Spliterator拆分问题(不使用size())
我明白你卡在这里的痛点——LinkedList没法随机访问,又不能用size(),要实现按规则拆分确实有点绕。咱们一步步来拆解这个问题:
核心思路
因为不能用size(),所以必须通过遍历计数来判断是否满足拆分条件:当当前Spliterator的起始位置往后数至少有5个元素时,拆分出前4个,原Spliterator从第5个元素开始继续遍历;否则返回null。
LinkedList的遍历只能靠节点移动或者Iterator,这里提供两种实现方案,分别对应高效的内部节点操作和通用的Iterator缓存方案。
方案一:直接操作LinkedList内部节点(高效)
如果你的代码和LinkedList同包(或者能通过反射访问内部节点),可以直接用节点来跟踪遍历位置,效率最高:
import java.util.LinkedList; import java.util.Spliterator; import java.util.function.Consumer; public class LinkedListSpliterator<E> implements Spliterator<E> { private LinkedList.Node<E> currentNode; // 从LinkedList初始化 public LinkedListSpliterator(LinkedList<E> list) { this.currentNode = list.getFirst(); } // 用于拆分的私有构造方法 private LinkedListSpliterator(LinkedList.Node<E> currentNode) { this.currentNode = currentNode; } @Override public boolean tryAdvance(Consumer<? super E> action) { if (currentNode == null) return false; action.accept(currentNode.item); currentNode = currentNode.next; return true; } @Override public void forEachRemaining(Consumer<? super E> action) { LinkedList.Node<E> node = currentNode; while (node != null) { action.accept(node.item); node = node.next; } currentNode = null; // 标记为已遍历完成 } @Override public Spliterator<E> trySplit() { if (currentNode == null) return null; // 遍历到第4个节点,同时检查是否存在第5个节点 LinkedList.Node<E> splitEnd = currentNode; int count = 1; while (splitEnd.next != null && count < 4) { splitEnd = splitEnd.next; count++; } // 没有第5个节点,不满足拆分条件 if (splitEnd.next == null) return null; // 保存原Spliterator的新起点(第5个节点) LinkedList.Node<E> newCurrent = splitEnd.next; splitEnd.next = null; // 断开拆分部分和原链表的连接,避免遍历越界 // 创建并返回拆分出的前4个元素的Spliterator LinkedListSpliterator<E> splitSpliterator = new LinkedListSpliterator<>(currentNode); this.currentNode = newCurrent; return splitSpliterator; } @Override public long estimateSize() { return Spliterator.UNKNOWN_SIZE; // 无法提前知道大小 } @Override public int characteristics() { return Spliterator.ORDERED | Spliterator.NONNULL; // LinkedList是有序的,元素非空 } }
方案二:通用Iterator缓存方案(无需访问内部节点)
如果没法访问LinkedList的内部节点,用Iterator配合缓存来实现,兼容性更强:
import java.util.LinkedList; import java.util.List; import java.util.Spliterator; import java.util.function.Consumer; import java.util.ArrayList; import java.util.Iterator; public class LinkedListSpliterator<E> implements Spliterator<E> { private final Iterator<E> iterator; private List<E> buffer; // 缓存拆分失败时取出的元素 public LinkedListSpliterator(LinkedList<E> list) { this.iterator = list.iterator(); this.buffer = null; } // 用于拆分的私有构造方法 private LinkedListSpliterator(List<E> buffer) { this.iterator = null; this.buffer = buffer; } @Override public boolean tryAdvance(Consumer<? super E> action) { // 优先处理缓存的元素 if (buffer != null) { if (!buffer.isEmpty()) { action.accept(buffer.remove(0)); return true; } buffer = null; } return iterator.hasNext() && (action.accept(iterator.next()) != null); } @Override public void forEachRemaining(Consumer<? super E> action) { if (buffer != null) { buffer.forEach(action); buffer = null; } iterator.forEachRemaining(action); } @Override public Spliterator<E> trySplit() { if (iterator == null || !iterator.hasNext()) return null; // 收集前4个元素 List<E> splitElements = new ArrayList<>(4); for (int i = 0; i < 4 && iterator.hasNext(); i++) { splitElements.add(iterator.next()); } // 收集4个元素后没有剩余,说明总元素不足5个,把元素放回缓存 if (!iterator.hasNext()) { buffer = splitElements; return null; } // 满足条件,返回拆分出的Spliterator return new LinkedListSpliterator<>(splitElements); } @Override public long estimateSize() { return Spliterator.UNKNOWN_SIZE; } @Override public int characteristics() { return Spliterator.ORDERED | Spliterator.NONNULL; } }
如何获取所有拆分批次
要拿到所有可拆分的批次,需要循环拆分每个Spliterator直到无法拆分为止,把所有拆分出的和剩余的Spliterator收集起来:
import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Spliterator; public class Main { public static void main(String[] args) { LinkedList<Integer> list = new LinkedList<>(); for (int i = 1; i <= 12; i++) { list.add(i); } Spliterator<Integer> mainSpliterator = new LinkedListSpliterator<>(list); List<Spliterator<Integer>> allBatches = new ArrayList<>(); allBatches.add(mainSpliterator); // 循环拆分所有可拆分的Spliterator int index = 0; while (index < allBatches.size()) { Spliterator<Integer> current = allBatches.get(index); Spliterator<Integer> split = current.trySplit(); if (split != null) { allBatches.add(split); } else { index++; } } // 遍历所有批次 for (int i = 0; i < allBatches.size(); i++) { System.out.printf("批次 %d:", i + 1); allBatches.get(i).forEachRemaining(item -> System.out.print(item + " ")); System.out.println(); } } }
运行后会输出所有拆分后的批次,比如1-4、5-8、9-12这样的分组。
内容的提问来源于stack exchange,提问作者Andrei
相关产品推荐
相关产品推荐

