如何防止迭代器列表的变异?

f4t66c6m  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(420)

我希望避免迭代器输入列表的突变 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;
    }

}
vsaztqbk

vsaztqbk1#

这个问题似乎是基于误解。。。一两个。
如何防止迭代器列表的变异?
您需要区分列表的易变性和列表中项目的易变性。我想你实际上是在问后者(因此,这个清单与这个问题并不相关。我们将看到。)
我希望避免其他人对迭代器测试的输入列表进行变异。
同样,您似乎在询问列表,但我认为您实际上是想询问迭代器。
我只希望其他人在测试的深度副本上运行。
这意味着您希望迭代器是不可变的。
问题是:
Iterator 是一个固有的有状态/可变对象。实际上,没有办法实施 next() 不改变迭代器对象。 Iterator 对象通常不可深度复制。他们通常不支持 clone() 或者公共构造函数,它们通常不实现 Serializable . (事实上,如果它们是可序列化的,序列化/反序列化的语义将是有问题的。)
所以基本上,你的不可变迭代器列表或者(以某种方式)产生迭代器深度副本的列表的想法是不实际的。
您评论:
所以呢 List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests); 无法为生成不可修改的列表 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 ...
}

如果您的迭代器无法重新创建(例如,如果您正在迭代一个无法创建或“重写”的源代码),您可以设想实现一个缓存迭代器 Package 器,它可以记住迭代序列中的所有元素,并且可以重置为序列的开头,或者生成一个新的迭代器来重放序列(但这就太过分了。)

相关问题