I would like to remove an element from an ArrayList in Java if it meets a certain criteria.
ie:
for (Pulse p : pulseArray) {
if (p.getCurrent() == null) {
pulseArray.remove(p);
}
}
I can understand why this does not work, but what is a good way to do this?
You can use Collection::removeIf(Predicate filter) (available from Java8 onwards), here is a simple example:
final Collection<Integer> list = new ArrayList<>(Arrays.asList(1, 2));
list.removeIf(value -> value < 2);
System.out.println(list); // outputs "[2]"
You must use an Iterator to iterate and the remove function of the iterator (not of the list) :
Iterator<Pulse> iter = pulseArray.iterator();
while (iter.hasNext()) {
Pulse p = iter.next();
if (p.getCurrent()==null) iter.remove();
}
Note that the Iterator#remove function is said to be optionnal but it is implemented by the ArrayList's iterator.
Here's the code of this concrete function from ArrayList.java :
765 public void remove() {
766 if (lastRet < 0)
767 throw new IllegalStateException();
768 checkForComodification();
769
770 try {
771 ArrayList.this.remove(lastRet);
772 cursor = lastRet;
773 lastRet = -1;
774 expectedModCount = modCount;
775 } catch (IndexOutOfBoundsException ex) {
776 throw new ConcurrentModificationException();
777 }
778 }
779
780 final void checkForComodification() {
781 if (modCount != expectedModCount)
782 throw new ConcurrentModificationException();
783 }
784 }
The expectedModCount = modCount; line is why it won't throw an exception when you use it while iterating.
No need to use iterator. With Java 8 (streaming and filtering capability and lambdas) you can accomplish it using one line.
For eg. the required code that does the operation you specified will be :
pulseArray = pulseArray.stream().filter(pulse -> pulse != null).collect(Collectors.toList());
You can implement interface Predicate overriding abstract method boolean test(T);
Use removeIf(Predicate p) method to remove all matching elements from your
list.
For example:
List<Book> bookList = new ArrayList<>();
bookList.add(new Book(101, "bookname1"));
bookList.add(new Book(102, "booknamelong2"));
bookList.removeIf(new LongBookNames())
public class LongBookNames implements Predicate<Book> {
#Override
public boolean test(Book book) {
return book.getBookName.length() >10;
}
}
When you are removing the element from the same list, the index gets disturbed. Try little differently as below:
for (int i=0; i < pulseArray.size(); i++) {
Pulse p = (Pulse)pulseArray.get(i);
if (p.getCurrent() == null) {
pulseArray.remove(p);
i--;//decrease the counter by one
}
}
As an alterative to using an iterator, you can use the Guava collections library. This has the advantage of being more functional (if you are into that sort of thing):
Predicate<Pulse> hasCurrent = new Predicate<Pulse>() {
#Override public boolean apply(Pulse input) {
return (input.getCurrent() != null);
}
};
pulseArray = Lists.newArrayList(Collections2.filter(pulseArray, hasCurrent));
You can't alter a collection that you're iterating through using methods on the collection. However, some iterators (including iterators on ArrayLists) support a remove() method that allows you to remove methods in the order that you're iterating.
Iterator<Pulse> iterator = pulseArray.iterator();
while (iterator.hasNext()) {
Pulse p = iterator.next();
if (p.getCurrent() == null) {
iterator.remove();
}
}
Below one is used when Single ArrayList have multiple types Objects and one object have count == 0 then it removed from pulseArray
Constants.java
public class ViewType {
public static final int PULSE = 101;
public static final int HEARTBEAT = 102;
}
BaseModel.java (This is Base Model)
public interface BaseModel {
int getViewType();
}
PulseModel.java (which implements with BaseModel)
public class PulseModel implements BaseModel {
#Override
public int getViewType() {
return Constants.ViewType.PULSE;
}
#SerializedName("PulseId")
#Expose
private String pulseId;
#SerializedName("Count")
#Expose
private String count;
}
Remove PulseModel object from pulseArray in which Count = 0
pulseArray.removeIf(
(BaseModel model) -> {
boolean remove = false;
if (model instanceof PulseModel) {
remove = (((PulseModel) model).getCount() == 0);
if (remove) {
//Success
}
}
return remove;
});
To remove elements from ArrayList based on a condition or predicate or filter, use removeIf() method. You can call removeIf() method on the ArrayList, with the predicate (filter) passed as argument. All the elements that satisfy the filter (predicate) will be removed from the ArrayList.
arraylist.removeIf(element -> (Objects.equals(element.getId(), id)));
Using an Iterator would give you the power of modifying the list while iterating through the arraylist
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.)
I have something like List<List<UsersDetails>> userList. If I debug to see its value it's giving [[]] i.e., List<UsersDetails> is empty and List<List<UsersDetails>> is also empty. Is there a way to check if List<UsersDetails> is empty without iteration?
I tried userList.sizeOf, userList.empty() functions and userList==null operator but all are giving false.
If you want to check each element in the "outer" list you have to iterate over it somehow. Java 8's streams would hide this from you, though, and provide a slightly cleaner syntax:
boolean allEmpty = userList.stream().allMatch(l -> l == null || l.empty());
There is not. There is:
if (userList.isEmpty() || userList.get(0).isEmpty()) { ... }
But mostly if the notion: "This a list of lists where the list of lists contains 1 list, but that list is empty" is something you should consider as 'empty', you're using the wrong datastructure. You haven't explained what you are modelling with this List<List<UsersDetails>> but perhaps if you elaborate on that, some other data type in java.* or perhaps guava would be far more suitable. For example, maybe a Map<Integer, UsersDetail> is a better match here (mapping a user's ID to their details).
You could create your own List that simply delegates to e.g. ArrayList but prevents null or empty lists from being added:
public class NonEmptyUserList implements List<List<UserDetails>>{
private ArrayList<List<String>> mDelegate = new ArrayList<>();
public void add(int index, List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return;
}
mDelegate.add(index, element);
}
public boolean add(List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return false;
}
return mDelegate.add(e);
}
public List<UserDetails> set(int index, List<UserDetails> element) {
if (element == null || element.isEmpty()) {
return null;
}
return mDelegate.set(index, element);
}
public boolean addAll(Collection<? extends List<UserDetails>> c) {
boolean changed = false;
for (final List<String> list : c) {
changed = changed || add(list);
}
return changed;
}
public boolean addAll(int index, Collection<? extends List<UserDetails>> c) {
boolean changed = false;
int startIndex = index;
for (final List<String> list : c) {
add(startIndex, list);
changed = changed || (list != null) && !list.isEmpty();
startIndex++;
}
return changed;
}
// delegate all other methods required by `List` to mDelegate
}
Using this list you can be sure no null or empty values will be present and thus you can use:
NonEmptyUserList userList = new NonEmptyUserList();
userList.add(null);
userList.add(Collections.emptyList());
userList.isEmpty(); // returns true
List<UserDetails> subList = new ArrayList<>();
subList.add(null);
userList.add(subList);
userList.isEmpty(); // returns false
If you want to handle sub lists with only null elements as empty as well you will need to extend the above implementation. This is however the only solution I can currently imagine that doesn't involve iterating over the list's elements. But I'd not really recommend this solution. I just wrote it down to show you what might be possible.
I personally think the answer provided by #Mureinik using streams is most favorable.
This has been annoying me in a project recently and my Google phoo is failing me at finding a suitable answer.
Is there a collection, that has access to the ListIterator but also only allows for unique values inside the collection?
Reasoning for this, I have a collection of items, that in this collection, there should only ever be one of each element. I also want to be able to traverse this collection in both directions whilst also being sorted or allow me to sort it using Collections.Sort();
I've not found anything suitable and had to write my own class using the following code:
public class UniqueArrayList<E> extends ArrayList<E> {
#Override
public boolean add(E element){
if (this.contains(element))
return false;
else
return super.add(element);
}
#Override
public void add(int index, E element){
if (this.contains(element))
return;
else
super.add(index, element);
}
#Override
public boolean addAll(Collection<? extends E> c){
if (new HashSet<E>(c).size() < c.size())
return false;
for(E element : c){
if (this.contains(c))
return false;
}
return super.addAll(c);
}
#Override
public boolean addAll(int index, Collection<? extends E> c) {
if (new HashSet<E>(c).size() < c.size())
return false;
for(E element : c){
if (this.contains(c))
return false;
}
return super.addAll(index, c);
}
#Override
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > this.size())
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
#Override
public ListIterator<E> listIterator() {
return new ListItr(0);
}
#Override
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
#SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size())
throw new NoSuchElementException();
Object[] elementData = UniqueArrayList.this.toArray();
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
UniqueArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
#SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = UniqueArrayList.this.toArray();
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//Need to allow this for the collections sort to work!
//if (!UniqueArrayList.this.contains(e))
UniqueArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
UniqueArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
However this is far from perfect, for one I can't override the ListIterator.set(); because Collections.sort(); uses it to move items in the list. If I try to prevent non unique items from being added to the list here, the sort never happens.
So, does anyone have a better method or know of another collection that abides by the rules that I would like? Or do I just need to live with this rather irritating issue?
[Edit]
This is the Collections.sort(); method:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object[] a = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
The reasoning they give for doing this is:
This implementation dumps the specified list into an array, sorts the
array, and iterates over the list resetting each element from the
corresponding position in the array. This avoids the n2
log(n) performance that would result from attempting to sort a linked
list in place.
When you need unique values you should try to switch to a Set.
You can use a TreeSet together with a Comparator instance to sort the entries. The descendingSet() method of TreeSet will give you the reverse order.
If you really need a ListIterator at some point you could create a temporary list from the set.
As Thor Stan mentioned, a TreeSet gets you most of what you want. It ensures elements are unique, it keeps them sorted, and you can iterate it in either direction using iterator() or descendingIterator().
It's not entirely clear why you're asking for ListIterator though. Most things about a ListIterator are very positional: the notion of an index, or adding something at the current position, or setting the current element. These don't make sense for a sorted set.
One aspect of a ListIterator that you might be looking for, though, is the ability to reverse directions in the midst of iteration. You can't do this directly with a TreeSet iterator, since it offers access only via an ordinary Iterator instead of a ListIterator.
However, a TreeSet implements the NavigableSet interface, which lets you step through the elements in order, in either direction. The NavigableSet interface is a subinterface of SortedSet, which provides the first() and last() methods to get you started at one of the "ends" of the set. Once you have an element in the set, you can step in either direction using the lower(E) and higher(E) methods. Or, if you want to start somewhere in the middle, you can't start at a position by index, but you can start with a value (which needn't be a member of the set) and then call lower(E) or higher(E).
For example:
TreeSet<String> set = new TreeSet<>(
Arrays.asList("a", "z", "b", "y"));
String cur;
cur = set.first(); // a
cur = set.higher(cur); // b
cur = set.higher(cur); // y
cur = set.higher(cur); // z
cur = set.lower(cur); // y
cur = set.lower(cur); // b
cur = set.lower(cur); // a
cur = set.lower(cur); // null
Java considers List to allow non-unique items and Set to not. Sets obviously don't support ListIterators and therefore code that uses ListIterator can assume that the underlying collection is not a Set.
Java 8 doesn't use ListIterator for sorting anymore, but if you're stuck with Java 7 that doesn't really help you. Basically depending a lot on your context and usage, it might be useful to either use a Set like Thor Stan said in his response, creating a List on demand when needed.
Another option is to just provide your own ListIterator that accesses the list through a method that doesn't check for duplicates. This would have the advantage of not creating extraneous objects, but the Set option would most likely result in shorter code and is highly unlikely to be significant performance-wise.
There are probably other options too, but I can't think of any elegant ones.
This is a problem with no easy answers.
The problem is that you have broken the contract for add(int, E). This method must either add the element or throw an exception - it is not allowed to return without adding the element.
If you override set(int, E) so that sometimes it doesn't set the element, it would break the contract for that method too, and it would prevent Collections.sort() from working as you identified.
I would not recommend breaking these contracts - it may cause other methods that act on lists to behave in unpredictable ways.
Others have experienced these difficulties - see the java docs for SetUniqueList for example.
Another problem with your implementation is that it would be extremely slow because ArrayList.contains() is a linear search.
One solution would be to write a class that uses both an ArrayList and a HashSet, and write your own versions of add and set, rather than breaking the contracts for the usual versions. This is not tested, but you get the idea.
public final class MyList<E extends Comparable<? super E>> extends AbstractList<E> {
private final List<E> list = new ArrayList<>();
private final Set<E> set = new HashSet<>();
public E get(int i) {
return list.get(i);
}
public int size() {
return list.size();
}
public boolean tryAdd(E e) {
return set.add(e) && list.add(e);
}
public boolean tryAdd(int i, E e) {
if (set.add(e)) {
list.add(i, e);
return true;
}
return false;
}
public boolean trySet(int i, E e) {
return set.add(e) && set.remove(list.set(i, e));
}
public boolean remove(Object o) {
return set.remove(o) && list.remove(o);
}
public void sort() {
Collections.sort(list);
}
// One bonus of this approach is that contains() is now O(1)
public boolean contains(Object o) {
return set.contains(o);
}
// rest omitted.
}
Something like this would not break the contract for List. Note that with this version, add and set throw an UnsupportedOperationException, because this is the behaviour inherited from AbstractList. You would be able to sort by calling myList.sort(). Also, the listIterator method would work, but you would not be able to use it to set or add (although remove would work).
If you needed to add or set elements while iterating over this List, you would need to use an explicit index, rather than a ListIterator. Personally, I do not consider this a major problem. ListIterator is necessary for lists like LinkedList that do not have a constant time get method, but for ArrayList it's nice but not essential.
I was recently asked about the question that how to create a Java Iterator for 2D Array, specifically how to implement:
public class PersonIterator implements Iterator<Person>{
private List<List<Person>> list;
public PersonIterator(List<List<Person>> list){
this.list = list;
}
#Override
public boolean hasNext() {
}
#Override
public Person next() {
}
}
1D array is pretty straightforward by using a index to track the position, any idea about how to do it for 2D lists.
In the 1D case you need to keep one index to know where you left, right?
Well, in the 2D case you need two indices: one to know in which sub-list you were working, and other one to know at what element inside that sub-list you left.
Something like this? (Note: untested)
public class PersonIterator implements Iterator<Person>{
// This keeps track of the outer set of lists, the lists of lists
private Iterator<List<Person>> iterator;
// This tracks the inner set of lists, the lists of persons we're going through
private Iterator<Person> curIterator;
public PersonIterator(List<List<Person>> list){
// Set the outer one
this.iterator = list.iterator();
// And set the inner one based on whether or not we can
if (this.iterator.hasNext()) {
this.curIterator = iterator.next();
} else {
this.curIterator = null;
}
}
#Override
public boolean hasNext() {
// If the current iterator is valid then we obviously have another one
if (curIterator != null && curIterator.hasNext()) {
return true;
// Otherwise we need to safely get the iterator for the next list to iterate.
} else if (iterator.hasNext()) {
// We load a new iterator here
curIterator = iterator.next();
// and retry peeking to see if the new curIterator has any elements to iterate.
return hasNext();
// Otherwise we're out of lists.
} else {
return false;
}
}
#Override
public Person next() {
// Return the current value off the inner iterator if we can
if (curIterator != null && curIterator.hasNext()) {
return curIterator.next();
// Otherwise try to iterate along the next list and retry getting the next one.
// This won't infinitely loop at the end since next() at the end of the outer
// iterator should result in an NoSuchElementException.
} else {
curIterator = iterator.next();
return next();
}
}
}
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.