I was asked the following question in an interview:
Combine two iterators over their sorted contents such that the
resulting iterator should iterate over the combination of these 2
iterators in sorted order in O(1) time (these iterators iterate over a
String).
I wrote the below code but I'm sure it doesn't perform in O(1) time. What advice do you have for matching the constraints set by the interview question?
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class iteratorCombine {
// assumption1: elements are hardcoded
// assumption2: both iterators have equal number of elements
public static void main(String[] args) {
iteratorCombine testObj = new iteratorCombine();
Set<String> firstSet = new TreeSet<String>();
Set<String> secondSet = new TreeSet<String>();
Set<String> combinedSet;
firstSet = testObj.storeElements1(firstSet);
secondSet = testObj.storeElements2(secondSet);
Iterator<String> it1 = firstSet.iterator();
Iterator<String> it2 = secondSet.iterator();
combinedSet = testObj.combine(it1, it2);
// output
Iterator<String> itComb = combinedSet.iterator();
while(itComb.hasNext()){
System.out.println(itComb.next());
}
}
public Set<String> storeElements1(Set<String> firstSet){
firstSet.add("first3");
firstSet.add("first1");
firstSet.add("first2");
return firstSet;
}
public Set<String> storeElements2(Set<String> secondSet){
secondSet.add("second3");
secondSet.add("second1");
secondSet.add("second2");
return secondSet;
}
public Set<String> combine(Iterator<String> it1, Iterator<String>it2){
String firstEle, secondEle;
Set<String> combinedSet = new TreeSet<String>();
while (it1.hasNext() && it2.hasNext()) {
firstEle = it1.next();
secondEle = it2.next();
combinedSet.add(firstEle+secondEle);
}
return combinedSet;
}
}
I believe that you can't do it if you don't extend iterator and support a peek function. Such an iterator is not that hard. Here is a way for doing it.
static class PeekingIterator<T> implements Iterator<T> {
private final Iterator<T> iterator;
private T temp;
public PeekingIterator(Iterator<T> iterator) {
this.iterator = iterator;
}
public T peek() {
//if there is no peek, advance the iterator and store its value, return the peek otherwise
if(temp==null){
temp = this.iterator.next();
}
return temp;
}
#Override
public T next() {
//if we already have a peek,return it and nullify it, otherwise do normal next()
if(temp!=null){
T t = temp;
temp = null;
return t;
}else{
return this.iterator.next();
}
}
#Override
public boolean hasNext() {
return this.iterator.hasNext() || temp!=null;
}
}
Once you can peek, the rest is easy, you can build SortedIterator using two peeking iterators, peek both iterators and advance the iterator that has the smaller element.
static class SortedIterator<T extends Comparable<T>> implements Iterator<T>{
private final PeekingIterator<T> peekingIterator1;
private final PeekingIterator<T> peekingIterator2;
SortedIterator(Iterator<T> source1, Iterator<T> source2){
peekingIterator1 = new PeekingIterator<>(source1);
peekingIterator2 = new PeekingIterator<>(source2);
}
#Override
public boolean hasNext() {
return peekingIterator1.hasNext() || peekingIterator2.hasNext();
}
#Override
public T next() {
if(!peekingIterator1.hasNext()){
return peekingIterator2.next();
}
if(!peekingIterator2.hasNext()){
return peekingIterator1.next();
}
T peek1 = peekingIterator1.peek();
T peek2 = peekingIterator2.peek();
if(peek1.compareTo(peek2)<0){
return peekingIterator1.next();
}
return peekingIterator2.next();
}
}
The analysis are obvious here, SortedIterator.next and SortedIterator.hasNext run in constant time.
I had a similar use case but instead of only 2 iterators, I had to merge a dynamic number of iterators. The number of iterators can be more than 2. e.g., 3 iterators, 4 iterators, or more.
I used the same solution as Sleiman Jneidi suggested. I modified the SortedIterator to support multiple iterators and also support sorting in ascending or descending order based on need.
For the PeekingIterator, I used Apache commons collection's Peeking iterator.
Here is my sorted iterator if someone may need to merge multiple sorted iterators, they can refer to this:
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.collections4.iterators.PeekingIterator;
/**
* This class is special implementation of Iterator.
* It lets multiple sorted iterators to be merged to a single sorted iterator.
* It will use given comparator to compare values from multiple iterators.
*
*/
public class SortedMergedIterator<T> implements Iterator<T> {
/**
* Comparator that will be used to compare values from across the iterators.
*/
private final Comparator<? super T> comparator;
/**
* List of sorted iterators which are required to be merged to a single sorted iterator.
*/
private final List<PeekingIterator<T>> peekingIterators;
/**
* By default the sort order will be considered as ascending order.
*
* It this flag is set to true, the elements will be sorted in descending order.
* In this case, it is pre-requisite that all the iterators being passed should be sorted in ascending order.
*/
private final boolean sortOrderDescending;
public SortedMergedIterator(final Comparator<? super T> comparator, final List<Iterator<T>> iterators) {
this(comparator, iterators, false);
}
public SortedMergedIterator(final Comparator<? super T> comparator, final List<Iterator<T>> iterators, final boolean sortOrderDescending) {
this.comparator = comparator;
this.peekingIterators = iterators.stream().map(iterator -> new PeekingIterator<>(iterator)).collect(Collectors.toList());
this.sortOrderDescending = sortOrderDescending;
}
#Override
public boolean hasNext() {
// If at least one of the child iterator has next element.
return this.peekingIterators.stream().anyMatch(Iterator::hasNext);
}
#Override
public T next() {
// Peek next value from all the iterators.
final List<T> peekedValues = this.peekingIterators.stream().map(PeekingIterator::peek).collect(Collectors.toList());
// Find the minimum value from all the peeked values.
final T minElement = peekedValues
.stream()
.filter(Objects::nonNull)
.min(this.sortOrderDescending ? this.comparator.reversed() : this.comparator)
.orElse(null);
// Return the next element from an iterator for which minimum value is found.
return this.peekingIterators.get(peekedValues.indexOf(minElement)).next();
}
}
Example of using this iterator:
public static void main(String[] args) {
// Example of ascending order sorted iterators.
final List<Integer> list1 = Lists.newArrayList(4,7,11,12,16);
final List<Integer> list2 = Lists.newArrayList(1,3,5,10,15);
final List<Integer> list3 = Lists.newArrayList(6,8,13,18,20);
final List<Integer> list4 = Lists.newArrayList(2,9,14,17,19);
final SortedMergedIterator<Integer> sortedIterator =
new SortedMergedIterator<>(Comparator.comparingInt(a -> a), Arrays.asList(list1.iterator(), list2.iterator(), list3.iterator(), list4.iterator()));
while (sortedIterator.hasNext()) {
System.out.println(sortedIterator.next());
}
System.out.println();
// Example of descending order sorted iterators.
final List<Integer> list5 = Lists.newArrayList(16,12,11,7,4);
final List<Integer> list6 = Lists.newArrayList(15,10,5,3,1);
final List<Integer> list7 = Lists.newArrayList(20,18,13,8,6);
final List<Integer> list8 = Lists.newArrayList(19,17,14,9,2);
final SortedMergedIterator<Integer> descSortedIterator =
new SortedMergedIterator<>(Comparator.comparingInt(a -> a), Arrays.asList(list5.iterator(), list6.iterator(), list7.iterator(), list8.iterator()), true);
while (descSortedIterator.hasNext()) {
System.out.println(descSortedIterator.next());
}
}
Related
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.
How can this be achieved in Java?
Here is an example showing the effect of the mutation on tests. Both of the two parts are sorting the input. But the second part has nothing to be sorted since the mutation from the first part iterated the iterators to the end.
You can run the following example online here:
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;
}
}
This question seems to be based on a misunderstanding ... or two.
How can I prevent mutation of a list of iterators?
You need to distinguish between the mutability of a list, and the mutability of the items in the list. I think you are actually asking about the latter. (And as such, the list is not really relevant to the question. As we shall see.)
I would like to avoid the mutation of the input list of iterators tests by others.
Again, you appear to be asking about the list, but I think you actually mean to ask about the iterators.
I only want others to run on a deep copy of tests.
This implies you want the iterators to be immutable.
Here's the problem:
An Iterator is an inherently stateful / mutable object. Indeed, there is no way to implement next() without mutating the iterator object.
Iterator objects are typically not deep copyable. They typically don't support clone() or public constructors, and they typically do not implement Serializable. (Indeed, if they were serializable, the semantics of serialize / deserialize would be problematic.)
So basically, your idea of a list of immutable iterators or a list that (somehow) produces deep copies of iterators is not practical.
You commented:
So List<Iterator<Integer>> tests = Collections.unmodifiableList(mutableTests); cannot produce an unmodifiable list for List<Iterator<Integer>>?
Well, yes it can. But that doesn't solve the problem. You need a list of unmodifiable iterators rather than an unmodifiable list of iterators.
Possible solutions:
You could just recreate the list of iterators from their base collections for each test run.
Use Iterable instead of Iterator. The collection types you are using all implement Iterable, and the third iterator could be created from an empty list.
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 ...
}
If your iterators could not be recreated (for example, if you were iterating a source that couldn't be created or "rewound"), you could conceivably implement a caching iterator wrapper that remembered all of the elements in the iteration sequence and could either reset to the start of the sequence, or generate a new iterator to replay the sequence. (But that would be overkill here.)
Example: List 1: [1, 4, 5, 8, 9]
List 2: [3, 4, 4, 6]
List 3: [0, 2, 8]
Would yield the following result:
Iterator -> [0, 1, 2, 3, 4, 4, 4, 5, 6, 8, 8, 9]
I am reluctant to create a "merge" method that accepts the k lists and merges the contents of the List to another List in the spirit of space complexity. Is this a k-way merge problem that can be implemented using "min Heap". Any pointers would be very helpful.
public class CustomListIterator<E> implements Iterator<E>{
private boolean canAddIterators = true;
private boolean balanceTreeIteratorFlag = false;
private E f_element;
private E s_element;
private Iterator<E> left;
private Iterator<E> right;
private final Comparator<E> comparator;
public CustomListIterator(Comparator<E> comparator){
this.comparator = comparator;
}
public CustomListIterator(Iterator<E> left, Iterator<E> right, Comparator<E> comparator){
this.left = left;
this.right = right;
this.comparator = comparator;
}
public void addIterator(Iterator<E> iterator){
if (!canAddIterators)
throw new ConcurrentModificationException();
if (right == null){
right = iterator;
return;
}else if (left == null){
left = iterator;
return;
}
if (!balanceTreeIteratorFlag){
right = balanceTreeOfIterators(iterator, right);
}else{
left = balanceTreeOfIterators(iterator, left);
}
balanceTreeIteratorFlag = !balanceTreeIteratorFlag;
}
private Iterator<E> balanceTreeOfIterators(Iterator<E> iterator_1, Iterator<E> iterator_2){
if (iterator_2 instanceof CustomListIterator){
((CustomListIterator<E>)iterator_2).addIterator(iterator_1);
} else{
iterator_2 = new CustomListIterator<E>(iterator_1, iterator_2, comparator);
}
return iterator_2;
}
public boolean hasNext() {
if (canAddIterators){
if (left != null && left.hasNext()){
f_element = left.next();
}
if (right != null && right.hasNext()){
s_element = right.next();
}
}
canAddIterators = false;
return f_element != null || s_element != null;
}
public E next() {
E next;
if (canAddIterators){
if (left.hasNext()){
f_element = left.next();
}
if (right.hasNext()){
s_element = right.next();
}
}
canAddIterators = false;
if (s_element == null && f_element == null){
throw new NoSuchElementException();
}
if (f_element == null){
next = s_element;
s_element = right.hasNext() ? right.next() : null;
return next;
}
if (s_element == null){
next = f_element;
f_element = left.hasNext() ? left.next() : null;
return next;
}
return findNext();
}
public void remove() {
}
private E findNext(){
E next;
if (comparator.compare(f_element, s_element) < 0){
next = f_element;
f_element = left.hasNext() ? left.next() : null;
return next;
}
next = s_element;
s_element = right.hasNext() ? right.next() : null;
return next;
}
}
I don't this is the most optimal way of doing it (using a tree). Any suggestions on how this can be implemented only by overriding next() hasNext() and remove()?
There are basically three different ways to merge multiple sorted lists:
Successive two-way merges
Divide and conquer
Priority queue based
In the discussion below, n refers to the total number of items in all lists combined. k refers to the number of lists.
Case 1 is the easiest to envision, but also the least efficient. Imagine you're given four lists, A, B, C, and D. With this method, you merge A and B to create AB. Then you merge AB and C to create ABC. Finally, you merge ABC with D to create ABCD. The complexity of this algorithm approaches O(n*k). You iterate over A and B three times, C two times, and D one time.
The divide and conquer solution is to merge A and B to create AB. Then merge C and D to create CD. Then merge AB and CD to create ABCD. In the best case, which occurs when the lists have similar numbers of items, this method is O(n * log(k)). But if the lists' lengths vary widely, this algorithm's running time can approach O(n*k).
For more information about these two algorithms, see my blog entry, A closer look at pairwise merging. For more details about the divide and conquer approach specifically, see A different way to merge multiple lists.
The priority queue based merge works as follows:
Create a priority queue to hold the iterator for each list
while the priority queue is not empty
Remove the iterator that references the smallest current number
Output the referenced value
If not at end of iterator
Add the iterator back to the queue
This algorithm is proven to be O(n * log(k)) in the worst case. You can see that every item in every list is added to the priority queue exactly once, and removed from the priority queue exactly once. But the queue only contains k items at any time. So the memory requirements are very small.
The implementation of iterators in Java makes the priority queue implementation slightly inconvenient, but it's easily fixed with some helper classes. Most importantly, we need an iterator that lets us peek at the next item without consuming it. I call this a PeekableIterator, which looks like this:
// PeekableIterator is an iterator that lets us peek at the next item
// without consuming it.
public class PeekableIterator<E> implements Iterator<E> {
private final Iterator<E> iterator;
private E current;
private boolean hasCurrent;
public PeekableIterator(Iterator<E> iterator) {
this.iterator = iterator;
if (iterator.hasNext()) {
current = iterator.next();
hasCurrent = true;
}
else {
hasCurrent = false;
}
}
public E getCurrent() {
// TODO: Check for current item
return current;
}
public boolean hasNext() {
return hasCurrent;
}
public E next() {
// TODO: Error check to see if there is a current
E rslt = current;
if (iterator.hasNext()) {
current = iterator.next();
}
else {
hasCurrent = false;
}
return rslt;
}
public void remove() {
iterator.remove();
}
Then, since the priority queue will hold iterators rather than individual items, we need a comparator that will compare the current items of two PeekableIterator interfaces. That's easy enough to create:
// IteratorComparator lets us compare the next items for two PeekableIterator instances.
public class IteratorComparator<E> implements Comparator<PeekableIterator<E>> {
private final Comparator<E> comparator;
public IteratorComparator(Comparator<E> comparator) {
this.comparator = comparator;
}
public int compare(PeekableIterator<E> t1, PeekableIterator<E> t2) {
int rslt = comparator.compare(t1.getCurrent(), t2.getCurrent());
return rslt;
}
}
Those two classes are more formal implementations of the code you wrote to get and compare the next items for individual iterators.
Finally, the MergeIterator initializes a PriorityQueue<PeekableIterator> so that you can call the hasNext and next methods to iterate over the merged lists:
// MergeIterator merges items from multiple sorted iterators
// to produce a single sorted sequence.
public class MergeIterator<E> implements Iterator<E> {
private final IteratorComparator<E> comparator;
private final PriorityQueue<PeekableIterator<E>> pqueue;
// call with an array or list of sequences to merge
public MergeIterator(List<Iterator<E>> iterators, Comparator<E> comparator) {
this.comparator = new IteratorComparator<E>(comparator);
// initial capacity set to 11 because that's the default,
// and there's no constructor that lets me supply a comparator without the capacity.
pqueue = new PriorityQueue<PeekableIterator<E>>(11, this.comparator);
// add iterators to the priority queue
for (Iterator<E> iterator : iterators) {
// but only if the iterator actually has items
if (iterator.hasNext())
{
pqueue.offer(new PeekableIterator(iterator));
}
}
}
public boolean hasNext() {
return pqueue.size() > 0;
}
public E next() {
PeekableIterator<E> iterator = pqueue.poll();
E rslt = iterator.next();
if (iterator.hasNext()) {
pqueue.offer(iterator);
}
return rslt;
}
public void remove() {
// TODO: Throw UnsupportedOperationException
}
}
I've created a little test program to demonstrate how this works:
private void DoIt() {
String[] a1 = new String[] {"apple", "cherry", "grape", "peach", "strawberry"};
String[] a2 = new String[] {"banana", "fig", "orange"};
String[] a3 = new String[] {"cherry", "kumquat", "pear", "pineapple"};
// create an ArrayList of iterators that we can pass to the
// MergeIterator constructor.
ArrayList<Iterator<String>> iterators = new ArrayList<Iterator<String>> (
Arrays.asList(
Arrays.asList(a1).iterator(),
Arrays.asList(a2).iterator(),
Arrays.asList(a3).iterator())
);
// String.CASE_INSENSITIVE_ORDER is a Java 8 way to get
// a String comparator. If there's a better way to do this,
// I don't know what it is.
MergeIterator<String> merger = new MergeIterator(iterators, String.CASE_INSENSITIVE_ORDER);
while (merger.hasNext())
{
String s = merger.next();
System.out.println(s);
}
}
My performance comparisons of the divide-and-conquer and priority queue merges shows that the divide-and-conquer approach can be faster than using the priority queue, depending on the cost of comparisons. When comparisons are cheap (primitive types, for example), the pairwise merge is faster even though it does more work. As key comparisons become more expensive (like comparing strings), the priority queue merge has the advantage because it performs fewer comparisons.
More importantly, the pairwise merge requires twice the memory of the priority queue approach. My implementation used a FIFO queue, but even if I built a tree the pairwise merge would require more memory. Also, as your code shows, you still need the PeekableIterator and IteratorComparator classes (or something similar) if you want to implement the pairwise merge.
See Testing merge performance for more details about the relative performance of these two methods.
For the reasons I detailed above, I conclude that the priority queue merge is the best way to go.
In Java, I have several SortedSet instances. I would like to iterate over the elements from all these sets. One simple option is to create a new SortedSet, such as TreeSet x, deep-copy the contents of all the individual sets y_1, ..., y_n into it using x.addAll(y_i), and then iterate over x.
But is there a way to avoid deep copy? Couldn't I just create a view of type SortedSet which would somehow encapsulate the iterators of all the inner sets, but behave as a single set?
I'd prefer an existing, tested solution, rather than writing my own.
I'm not aware of any existing solution to accomplish this task, so I took the time to write one for you. I'm sure there's room for improvement on it, so take it as a guideline and nothing else.
As Sandor points out in his answer, there are some limitations that must be imposed or assumed. One such limitation is that every SortedSet must be sorted relative to the same order, otherwise there's no point in comparing their elements without creating a new set (representing the union of every individual set).
Here follows my code example which, as you'll notice, is relatively more complex than just creating a new set and adding all elements to it.
import java.util.*;
final class MultiSortedSetView<E> implements Iterable<E> {
private final List<SortedSet<E>> sets = new ArrayList<>();
private final Comparator<? super E> comparator;
MultiSortedSetView() {
comparator = null;
}
MultiSortedSetView(final Comparator<? super E> comp) {
comparator = comp;
}
#Override
public Iterator<E> iterator() {
return new MultiSortedSetIterator<E>(sets, comparator);
}
MultiSortedSetView<E> add(final SortedSet<E> set) {
// You may remove this `if` if you already know
// every set uses the same comparator.
if (comparator != set.comparator()) {
throw new IllegalArgumentException("Different Comparator!");
}
sets.add(set);
return this;
}
#Override
public boolean equals(final Object o) {
if (this == o) { return true; }
if (!(o instanceof MultiSortedSetView)) { return false; }
final MultiSortedSetView<?> n = (MultiSortedSetView<?>) o;
return sets.equals(n.sets) &&
(comparator == n.comparator ||
(comparator != null ? comparator.equals(n.comparator) :
n.comparator.equals(comparator)));
}
#Override
public int hashCode() {
int hash = comparator == null ? 0 : comparator.hashCode();
return 37 * hash + sets.hashCode();
}
#Override
public String toString() {
return sets.toString();
}
private final static class MultiSortedSetIterator<E>
implements Iterator<E> {
private final List<Iterator<E>> iterators;
private final PriorityQueue<Element<E>> queue;
private MultiSortedSetIterator(final List<SortedSet<E>> sets,
final Comparator<? super E> comparator) {
final int n = sets.size();
queue = new PriorityQueue<Element<E>>(n,
new ElementComparator<E>(comparator));
iterators = new ArrayList<Iterator<E>>(n);
for (final SortedSet<E> s: sets) {
iterators.add(s.iterator());
}
prepareQueue();
}
#Override
public E next() {
final Element<E> e = queue.poll();
if (e == null) {
throw new NoSuchElementException();
}
if (!insertFromIterator(e.iterator)) {
iterators.remove(e.iterator);
}
return e.element;
}
#Override
public boolean hasNext() {
return !queue.isEmpty();
}
private void prepareQueue() {
final Iterator<Iterator<E>> iterator = iterators.iterator();
while (iterator.hasNext()) {
if (!insertFromIterator(iterator.next())) {
iterator.remove();
}
}
}
private boolean insertFromIterator(final Iterator<E> i) {
while (i.hasNext()) {
final Element<E> e = new Element<>(i.next(), i);
if (!queue.contains(e)) {
queue.add(e);
return true;
}
}
return false;
}
private static final class Element<E> {
final E element;
final Iterator<E> iterator;
Element(final E e, final Iterator<E> i) {
element = e;
iterator = i;
}
#Override
public boolean equals(final Object o) {
if (o == this) { return true; }
if (!(o instanceof Element)) { return false; }
final Element<?> e = (Element<?>) o;
return element.equals(e.element);
}
}
private static final class ElementComparator<E>
implements Comparator<Element<E>> {
final Comparator<? super E> comparator;
ElementComparator(final Comparator<? super E> comp) {
comparator = comp;
}
#Override
#SuppressWarnings("unchecked")
public int compare(final Element<E> e1, final Element<E> e2) {
if (comparator != null) {
return comparator.compare(e1.element, e2.element);
}
return ((Comparable<? super E>) e1.element)
.compareTo(e2.element);
}
}
}
}
The inner workings of this class are simple to grasp. The view keeps a list of sorted sets, the ones you want to iterate over. It also needs the comparator that will be used to compare elements (null to use their natural ordering). You can only add (distinct) sets to the view.
The rest of the magic happens in the Iterator of this view. This iterator keeps a PriorityQueue of the elements that will be returned from next() and a list of iterators from the individual sets.
This queue will have, at all times, at most one element per set, and it discards repeating elements. The iterator also discards empty and used up iterators. In short, it guarantees that you will traverse every element exactly once (as in a set).
Here's an example on how to use this class.
SortedSet<Integer> s1 = new TreeSet<>();
SortedSet<Integer> s2 = new TreeSet<>();
SortedSet<Integer> s3 = new TreeSet<>();
SortedSet<Integer> s4 = new TreeSet<>();
// ...
MultiSortedSetView<Integer> v =
new MultiSortedSetView<Integer>()
.add(s1)
.add(s2)
.add(s3)
.add(s4);
for (final Integer i: v) {
System.out.println(i);
}
I do not think that is possible unless it is some special case, which would require custom implementation.
For example take the following two comparators:
public class Comparator1 implements Comparator<Long> {
#Override
public int compare(Long o1, Long o2) {
return o1.compareTo(o2);
}
}
public class Comparator2 implements Comparator<Long> {
#Override
public int compare(Long o1, Long o2) {
return -o1.compareTo(o2);
}
}
and the following code:
TreeSet<Long> set1 = new TreeSet<Long>(new Comparator1());
TreeSet<Long> set2 = new TreeSet<Long>(new Comparator2());
set1.addAll(Arrays.asList(new Long[] {1L, 3L, 5L}));
set2.addAll(Arrays.asList(new Long[] {2L, 4L, 6L}));
System.out.println(Joiner.on(",").join(set1.descendingIterator()));
System.out.println(Joiner.on(",").join(set2.descendingIterator()));
This will result in:
5,3,1
2,4,6
and is useless for any Comparator operating on the head element of the given Iterators.
This makes it impossible to create such a general solution. It is only possible if all sets are sorted using the same Comparator, however that cannot be guaranteed and ensured by any implementation which accept SortedSet objects, given multiple SortedSet instances (e.g. anything that would accept SortedSet<Long> instances, would accept both TreeSet objects).
A little bit more formal approach:
Given y_1,..,y_n are all sorted sets, if:
the intersect of these sets are an empty set
and there is an ordering of the sets where for every y_i, y_(i+1) set it is true that y_i[x] <= y_(i+1)[1] where x is the last element of the y_i sorted set, and <= means a comparative function
then the sets y_1,..,y_n can be read after each other as a SortedSet.
Now if any of the following conditions are not met:
if the first condition is not met, then the definition of a Set is not fulfilled, so it can not be a Set until a deep copy merge is completed and the duplicated elements are removed (See Set javadoc, first paragraph:
sets contain no pair of elements e1 and e2 such that e1.equals(e2)
the second condition can only be ensured using exactly the same comparator <= function
The first condition is the more important, because being a SortedSet implies being a Set, and if the definition of being a Set cannot be fulfilled, then the stronger conditions of a SortedSet definitely cannot be fulfilled.
There is a possibility that an implementation can exists which mimics the working of a SortedSet, but it will definitely not be a SortedSet.
com.google.common.collect.Sets#union from Guava will do the trick. It returns an unmodifiable view of the union of two sets. You may iterate over it. Returned set will not be sorted. You may then create new sorted set from returned set (new TreeSet() or com.google.common.collect.ImmutableSortedSet. I see no API to create view of given set as sorted set.
If your concern is a deep-copy on the objects passed to the TreeSet#addAll method, you shouldn't be. The javadoc does not indicate it's a deep-copy (and it certainly would say so if it was)...and the OpenJDK implementation doesn't show this either. No copies - simply additional references to the existing object.
Since the deep-copy isn't an issue, I think worrying about this, unless you've identified this as a specific performance problem, falls into the premature optimization category.
I am trying to write unit tests for a BinarySearchTree class
The keys() return an Iterable.It uses another class called Queue in which the keys are enqueued and returned..
The Queue (third party class) however doesn't have any equals() defined.
public class BinarySearchTree<Key extends Comparable<Key>,Value> {
Node root ;
private class Node{
private Key key;
private Value val;
private Node left;
private Node right;
private int N;
public Node(Key k, Value v,int N) {
super();
this.key = k;
this.val = v;
this.N = N;
}
}
public Iterable<Key> keys(){
Queue<Key> q = new Queue<Key>();
inOrder(root,q);
return q;
}
private void inOrder(Node x,Queue q){
if(x == null)return;
inOrder(x.left,q);
q.enqueue(x.key);
inOrder(x.right,q);
}
...
}
trying to write unit test
#Test
public void testKeys(){
MyBST<String, Integer> st = new MyBST<String, Integer>();
st.put("S",7);
st.put("E",2);
st.put("X",8);
st.put("A",3);
st.put("R",4);
st.put("C",1);
st.put("H",5);
st.put("M",6);
Queue<String> q = new Queue<String>();
q.enqueue("A");
q.enqueue("C");
q.enqueue("E");
q.enqueue("H");
q.enqueue("M");
q.enqueue("R");
q.enqueue("S");
q.enqueue("X");
Iterable<String> actual = st.keys();
assertEquals(q,actual);
}
This fails
java.lang.AssertionError: expected: std.Queue<A C E H M R S X > but was: std.Queue<A C E H M R S X >
at org.junit.Assert.fail(Assert.java:93)
at org.junit.Assert.failNotEquals(Assert.java:647)
at org.junit.Assert.assertEquals(Assert.java:128)
at org.junit.Assert.assertEquals(Assert.java:147)
at week4.MyBSTTests.testKeys(BSTTests.java:304)
Do I have to implement an equals() in the third party class or is there any other way to do this to check equality? All I could think of was running a loop dequeueing from queue q and comparing it with what the iterator returned.I am not sure if there is a better way.. Please advise..
Iterable<String> actual = st.keys();
Iterator<String> actualit = actual.iterator();
while(actualit.hasNext()){
String actualkey = actualit.next();
String exp = q.dequeue();
assertEquals(actualkey,exp);
}
Use Hamcrest's Matchers.contains (described here). For example:
assertThat(queue1.keys(), Matchers.contains("A", "C", "E", "H", "M", "R", "S", "X"));
This will check the elements that the Iterable returns without needing equality implemented on your queue class.
you can use the utility class java.util.Arrays.
From what I remember the Queue interface has a toArray method. So it would be something like this:
assertTrue(Arrays.equals(queue1.toArray(),queue2.toArray()));
As it is a third party library, you could use apache commons:
Object[] o = IteratorUtils.toArray(queue1.iterator());
Object[] o2 = IteratorUtils.toArray(queue1.iterator());
assertTrue(Arrays.equals(o,o2));
This is how I did this.
I converted the Iterable to the ArrayList. Then I made another arraylist of expected key values. That way I am able to check if two arrayLists are equal using assertEquals(arrayList1, arrayList2).
Here is the code I wrote for testing my preOrder traversal method.
import static org.junit.Assert.*;
import java.util.ArrayList;
import org.junit.Test;
public class BSTTest
{
BST<Integer, String> binaryTree = new BST<Integer, String>();
#Test
public void preOrdertest()
{
binaryTree.put(87, "Orange");
binaryTree.put(77, "Black");
binaryTree.put(81, "Green");
binaryTree.put(89, "Blue");
binaryTree.put(4, "Yellow");
binaryTree.put(26, "white");
binaryTree.put(23, "Purple");
binaryTree.put(27, "Violet");
binaryTree.put(57, "red");
binaryTree.put(1, "crimson");
ArrayList<Integer> testList = new ArrayList<>();
testList.add(87);
testList.add(77);
testList.add(4);
testList.add(1);
testList.add(26);
testList.add(23);
testList.add(27);
testList.add(57);
testList.add(81);
testList.add(89);
Iterable<Integer> actual = binaryTree.preOrder();
ArrayList<Integer> actualList = new ArrayList<>();
if (actual != null)
{
for (Integer e : actual)
actualList.add(e);
}
assertEquals(testList, actualList);
}
}
I'm writing a Java program in which I want to sort a set of items and get the N-highest elements of the set. The thing is, though, that I want the elements to be returned grouped by their rank -- so if I want the 3 highest elements, but there is a tie between two elements for third place, then the third result is a collection that contains the two tied elements.
I know I could write this myself, but I'm wondering if it's already been implemented somewhere else. Does anybody know of anything like this?
Sounds like the Google Collection's MultiMap might be what you're after.
Use the "rank" as your key when inserting your elements. Then sort the keys.
This is what I ended up going with:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.common.collect.Ordering;
public final class Sorting {
private Sorting() {}
public static <T extends Comparable<? super T>> List<List<T>> rank(
Iterable<T> iterable, int nRanks) {
if (nRanks < 0) {
throw new IllegalArgumentException(Integer.toString(nRanks));
}
if (nRanks == 0) {
return new ArrayList<List<T>>();
}
Iterator<T> iter = Ordering.natural().sortedCopy(iterable).iterator();
List<List<T>> ret = new ArrayList<List<T>>();
if (iter.hasNext()) {
T prev = iter.next();
List<T> group = new ArrayList<T>();
group.add(prev);
ret.add(group);
int rank = 1;
while (iter.hasNext()) {
T next = iter.next();
if (prev.compareTo(next) > 0) {
rank++;
if (rank > nRanks) {
break;
}
group = new ArrayList<T>();
ret.add(group);
}
group.add(next);
prev = next;
}
}
return ret;
}
}