如何防止迭代器列表发生突变?
How can I prevent mutation of a list of iterators?
我想避免其他人对迭代器的输入列表进行修改 tests
。我只希望其他人 运行 深拷贝 tests
.
如何在 Java 中实现?
这是一个显示突变对 tests
影响的示例。这两个部分都对输入进行排序。但是第二部分没有什么可排序的,因为第一部分的突变将迭代器迭代到最后。
您可以在此处在线 运行 以下示例:
https://onlinegdb.com/NC4WzLzmt
import java.util.*;
public class ImmutableExample {
public static void main(String[] args) {
System.out.println("sort on demand");
List<Iterator<Integer>> mutableTests = Arrays.asList(
Arrays.asList(1, 2).iterator(),
Arrays.asList(0).iterator(),
Collections.emptyIterator()
);
List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests);
MergingIterator mergingIterator = new MergingIterator(tests);
while (mergingIterator.hasNext()) {
System.out.println(mergingIterator.next());
}
System.out.println("sort all at once");
/* uncomment the following will see the same result:*/
// tests = Arrays.asList(
// Arrays.asList(1, 2).iterator(),
// Arrays.asList(0).iterator(),
// Collections.emptyIterator()
// );
MergeKSortedIterators sol = new MergeKSortedIterators();
Iterable<Integer> result = sol.mergeKSortedIterators(tests);
for (Integer num : result) {
System.out.println(num);
}
}
}
class PeekingIterator implements Iterator<Integer>, Comparable<PeekingIterator> {
Iterator<Integer> iterator;
Integer peekedElement;
boolean hasPeeked;
public PeekingIterator(Iterator<Integer> iterator) {
this.iterator = iterator;
}
public boolean hasNext() {
return hasPeeked || iterator.hasNext();
}
public Integer next() {
int nextElem = hasPeeked ? peekedElement : iterator.next();
hasPeeked = false;
return nextElem;
}
public Integer peek() {
peekedElement = hasPeeked ? peekedElement : iterator.next();
hasPeeked = true;
return peekedElement;
}
@Override
public int compareTo(PeekingIterator that) {
return this.peek() - that.peek();
}
}
class MergingIterator implements Iterator<Integer> {
Queue<PeekingIterator> minHeap;
public MergingIterator(List<Iterator<Integer>> iterators) {
// minHeap = new PriorityQueue<>((x, y) -> x.peek().compareTo(y.peek()));
minHeap = new PriorityQueue<>();
for (Iterator<Integer> iterator : iterators) {
if (iterator.hasNext()) {
minHeap.offer(new PeekingIterator(iterator));
}
}
}
public boolean hasNext() {
return !minHeap.isEmpty();
}
public Integer next() {
PeekingIterator nextIter = minHeap.poll();
Integer next = nextIter.next();
if (nextIter.hasNext()) {
minHeap.offer(nextIter);
}
return next;
}
}
class MergeKSortedIterators {
public Iterable<Integer> mergeKSortedIterators(List<Iterator<Integer>> iteratorList) {
List<Integer> result = new ArrayList<>();
if (iteratorList.isEmpty()) {
return result;
}
PriorityQueue<PeekingIterator> pq = new PriorityQueue<>();
for (Iterator<Integer> iterator : iteratorList) {
if (iterator.hasNext()) {
pq.add(new PeekingIterator(iterator));
}
}
while (!pq.isEmpty()) {
PeekingIterator curr = pq.poll();
// result.add(curr.peek());
// cannot use this one as hasNext() checks on `hasPeeked`
result.add(curr.next());
if (curr.hasNext()) {
pq.add(curr);
}
}
return result;
}
}
这个问题似乎是基于一个……或两个误解。
How can I prevent mutation of a list of iterators?
您需要区分列表的可变性和列表中项目的可变性。我认为您实际上问的是后者。 (因此,该列表与问题并不真正相关。正如我们将要看到的。)
I would like to avoid the mutation of the input list of iterators tests by others.
同样,您似乎在询问列表,但我认为您实际上意思是询问迭代器。
I only want others to run on a deep copy of tests.
这暗示您希望迭代器是不可变的。
这是问题所在:
Iterator
是一个固有的有状态/可变对象。事实上,如果不改变迭代器对象就无法实现 next()
。
Iterator
对象通常不可深度复制。它们通常不支持 clone()
或 public 构造函数,并且它们通常不实现 Serializable
。 (事实上 ,如果它们是可序列化的,那么序列化/反序列化的语义就会有问题。)
所以基本上,您关于不可变迭代器列表或(以某种方式)生成迭代器深层副本的列表的想法是不切实际的。
您发表评论:
So List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests);
cannot produce an unmodifiable list for List<Iterator<Integer>>
?
嗯,是的,可以。但这并不能解决问题。您需要 一个不可修改的迭代器列表,而不是一个不可修改的迭代器列表。
可能的解决方案:
您可以从每个测试的基础集合中重新创建迭代器列表 运行。
使用 Iterable
而不是 Iterator
。您正在使用的集合类型都实现了 Iterable
,第三个迭代器可以从空列表中创建。
List<Iterable<Integer>> tests = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(0),
Collections.emptyList()
);
// to use them ...
for (Iterable<Integer> iterable : tests) {
Iterator<Integer> iterator = iterable.iterator();
// etc ...
}
如果您的迭代器无法重新创建(例如,如果您正在迭代无法创建或“回绕”的源),您可以想像地实现一个缓存迭代器包装器来记住所有迭代序列中的元素,可以重置到序列的开始,或者生成一个新的迭代器来重播序列。 (但这在这里太过分了。)
我想避免其他人对迭代器的输入列表进行修改 tests
。我只希望其他人 运行 深拷贝 tests
.
如何在 Java 中实现?
这是一个显示突变对 tests
影响的示例。这两个部分都对输入进行排序。但是第二部分没有什么可排序的,因为第一部分的突变将迭代器迭代到最后。
您可以在此处在线 运行 以下示例: https://onlinegdb.com/NC4WzLzmt
import java.util.*;
public class ImmutableExample {
public static void main(String[] args) {
System.out.println("sort on demand");
List<Iterator<Integer>> mutableTests = Arrays.asList(
Arrays.asList(1, 2).iterator(),
Arrays.asList(0).iterator(),
Collections.emptyIterator()
);
List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests);
MergingIterator mergingIterator = new MergingIterator(tests);
while (mergingIterator.hasNext()) {
System.out.println(mergingIterator.next());
}
System.out.println("sort all at once");
/* uncomment the following will see the same result:*/
// tests = Arrays.asList(
// Arrays.asList(1, 2).iterator(),
// Arrays.asList(0).iterator(),
// Collections.emptyIterator()
// );
MergeKSortedIterators sol = new MergeKSortedIterators();
Iterable<Integer> result = sol.mergeKSortedIterators(tests);
for (Integer num : result) {
System.out.println(num);
}
}
}
class PeekingIterator implements Iterator<Integer>, Comparable<PeekingIterator> {
Iterator<Integer> iterator;
Integer peekedElement;
boolean hasPeeked;
public PeekingIterator(Iterator<Integer> iterator) {
this.iterator = iterator;
}
public boolean hasNext() {
return hasPeeked || iterator.hasNext();
}
public Integer next() {
int nextElem = hasPeeked ? peekedElement : iterator.next();
hasPeeked = false;
return nextElem;
}
public Integer peek() {
peekedElement = hasPeeked ? peekedElement : iterator.next();
hasPeeked = true;
return peekedElement;
}
@Override
public int compareTo(PeekingIterator that) {
return this.peek() - that.peek();
}
}
class MergingIterator implements Iterator<Integer> {
Queue<PeekingIterator> minHeap;
public MergingIterator(List<Iterator<Integer>> iterators) {
// minHeap = new PriorityQueue<>((x, y) -> x.peek().compareTo(y.peek()));
minHeap = new PriorityQueue<>();
for (Iterator<Integer> iterator : iterators) {
if (iterator.hasNext()) {
minHeap.offer(new PeekingIterator(iterator));
}
}
}
public boolean hasNext() {
return !minHeap.isEmpty();
}
public Integer next() {
PeekingIterator nextIter = minHeap.poll();
Integer next = nextIter.next();
if (nextIter.hasNext()) {
minHeap.offer(nextIter);
}
return next;
}
}
class MergeKSortedIterators {
public Iterable<Integer> mergeKSortedIterators(List<Iterator<Integer>> iteratorList) {
List<Integer> result = new ArrayList<>();
if (iteratorList.isEmpty()) {
return result;
}
PriorityQueue<PeekingIterator> pq = new PriorityQueue<>();
for (Iterator<Integer> iterator : iteratorList) {
if (iterator.hasNext()) {
pq.add(new PeekingIterator(iterator));
}
}
while (!pq.isEmpty()) {
PeekingIterator curr = pq.poll();
// result.add(curr.peek());
// cannot use this one as hasNext() checks on `hasPeeked`
result.add(curr.next());
if (curr.hasNext()) {
pq.add(curr);
}
}
return result;
}
}
这个问题似乎是基于一个……或两个误解。
How can I prevent mutation of a list of iterators?
您需要区分列表的可变性和列表中项目的可变性。我认为您实际上问的是后者。 (因此,该列表与问题并不真正相关。正如我们将要看到的。)
I would like to avoid the mutation of the input list of iterators tests by others.
同样,您似乎在询问列表,但我认为您实际上意思是询问迭代器。
I only want others to run on a deep copy of tests.
这暗示您希望迭代器是不可变的。
这是问题所在:
Iterator
是一个固有的有状态/可变对象。事实上,如果不改变迭代器对象就无法实现next()
。Iterator
对象通常不可深度复制。它们通常不支持clone()
或 public 构造函数,并且它们通常不实现Serializable
。 (事实上 ,如果它们是可序列化的,那么序列化/反序列化的语义就会有问题。)
所以基本上,您关于不可变迭代器列表或(以某种方式)生成迭代器深层副本的列表的想法是不切实际的。
您发表评论:
So
List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests);
cannot produce an unmodifiable list forList<Iterator<Integer>>
?
嗯,是的,可以。但这并不能解决问题。您需要 一个不可修改的迭代器列表,而不是一个不可修改的迭代器列表。
可能的解决方案:
您可以从每个测试的基础集合中重新创建迭代器列表 运行。
使用
Iterable
而不是Iterator
。您正在使用的集合类型都实现了Iterable
,第三个迭代器可以从空列表中创建。List<Iterable<Integer>> tests = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(0), Collections.emptyList() ); // to use them ... for (Iterable<Integer> iterable : tests) { Iterator<Integer> iterator = iterable.iterator(); // etc ... }
如果您的迭代器无法重新创建(例如,如果您正在迭代无法创建或“回绕”的源),您可以想像地实现一个缓存迭代器包装器来记住所有迭代序列中的元素,可以重置到序列的开始,或者生成一个新的迭代器来重播序列。 (但这在这里太过分了。)