I want to compute differences between collections. When using CollectionUtils.subtract() for custom comparison I need to override the object's equals() method. But what if I need to compare collections of objects of the same type but different comparison criterion? What about the Comparator interface, it seems perfectly suited here? AFAIK Comparator is mainly used for sorting. Isn't there a method that uses Comparators for subtracting?
static <Type> Collection<Type> subtract(Collection<Type> a, Collection<Type> b, Comparator<Type> c) {
Set<Type> subtrahend = new TreeSet<Type>(c);
subtrahend.addAll(b);
Collection<Type> result = new ArrayList<Type>();
for (Type item: a) {
if (!subtrahend.contains(item)) result.add(item);
}
return result;
}
The subtrahent tree-set is not necessary, but will improve performance for large b.
If you have an ArrayList, multiple removes can be more expensive than taking a copy.
List<Type> list = /* ArrayList */
Set<Type> toRemove = /* HashSet */
List<Type> copy = new ArrayList<Type>(list.size());
for(Type t: list)
if(!toRemove.contains(t))
copy.add(t);
list = copy;
Personally I would use a loop. Its likely to be shorter and clearer.
Collection<Type> collection =
for(Iterator<Type> i=collection.iterator(); i.hasNext();)
if (i.next() is to be removed)
i.remove();
The reason an Iterator is used explicitly is to use the Iterator.remove() which avoids a ConcurrentModificationException. Another way to avoid it is to use a copy of the collection which might be preferred.
for(Type t : new ArrayList<Type>(collection))
if (t is to be removed)
collection.remove(t);
This doesn't perform as well but may perform well enough.
Related
I have multiple lists and for a specific list (in the example list b), I must return an iterator which doesn't have/allow remove method, I must preserve the information:
public static void main(String[] args){
List<E> a = new ArrayList<E>;
List<E> b = new ArrayList<E>;
b.add("A");
b.add("B");
b.add("C");
myMethod();
}
public Iterator<E> myMethod(){
return b.iterator();
}
I can't change the "public Iterator myMethod()", how is possible to return the iterator for b, without Remove method?
return Collections.unmodifiableList(b).iterator();
This seems to me to be the easy way out. The unmodifiable view on your list ensures that the iterator you get cannot modify the list, that is, the remove method will not work and should throw an exception, an UnsupportedOperationException.
EDIT: Since you can’t change myMethod, there are two options, or really, variants:
Make b unmodifiable altogether. This will only be acceptable if you don’t need to make modifications of b at all, of course.
Make b an unmodifiable view onto a list that can be modified through some other view of the same list.
Code example for 1., in Java 8 and earlier:
List<String> b = Collections.unmodifiableList(Arrays.asList("A", "B", "C"));
In Java 9 and later it’s simpler:
List<String> b = List.of("A", "B", "C");
List.of produces an unmodifiable list.
In case a completely unmodifiable list doesn’t fit your requirements, here a suggestion for code fro option 2.:
List<String> modifiableB = new ArrayList(Arrays.asList("A", "B", "C"));
List<String> b = Collections.unmodifiableList(modifiableB);
Now myMethod will access b, that is, the unmodifiable view, and therefore produce an iterator that cannot remove elements, whereas other methods can modify b through modifiableB.
Just return the iterator of an unmodifiable list wrapping the original list:
return Collections.unmodifiableList(b).iterator();
The official documentation (archive) of containsAll only says "Returns true if this list contains all of the elements of the specified collection.". However, I just tested this:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(1);
List<Integer> list2 = new ArrayList<>();
list2.add(2);
list2.add(1);
list2.add(2);
System.out.println(list1.containsAll(list2));
The result is true, even though list1 does not contain a second 2.
So what is the official, completely defined behaviour of containsAll? Does it act as if all duplicates were removed from both lists? I remember reading somewhere that it can cause problems with duplicates, but I don't know the exact case.
The List.containsAll method behaves just as documented: it returns true if all the elements of the given collection belong to this collection, false otherwise. The docs say nothing about the order or cardinality of the elements.
The documentation for containsAll does not explicitly say how it determines whether an element belongs to the Collection. But the documentation for contains (which is implicitly specifying the semantics of "contains") does: it uses equals. Again, no mention of cardinality.
The containsAll method is declared in the Collection interface and re-declared in the List and Set interfaces, but it's first implemented in the Collection hierarchy by the AbstractCollection class, as follows:
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
As far as I know, this implementation is inherited by most common classes that implement the Collection interface in the Java Collections framework, except for the CopyOnWriteArrayList class and other specialized classes, such as empty lists and checked and immutable wrappers, etc.
So, if you look at the code, you'll see that it fulfils the docs you quoted:
Returns true if this list contains all of the elements of the specified collection.
In the docs of the AbstractList.containsAll method, there's also an #implSpec tag, which says the following:
#implSpec
This implementation iterates over the specified collection, checking each element returned by the iterator in turn to see if it's contained in this collection. If all elements are so contained true is returned, otherwise false.
With regard to possible optimizations, they're all relayed to the different implementations of the contains method, which is also implemented by AbstractCollection in a naive, brute-force-like way. However, contains is overriden in i.e. HashSet to take advantage of hashing, and also in ArrayList, where it uses indexes, etc.
You can iterate over one list and remove elements by value from another, then check if another list size == 0. If it is, then that means all second list elements were present in first list at least as many times as in the second list.
public boolean containsAll(List<Character> source, List<Character> target) {
for (Character character : source) {
target.remove(character);
if (target.isEmpty()) {
return true;
}
}
return target.size() == 0;
}
HashMap will be more efficient if lists are huge
public static boolean containsAll(List<Character> source, List<Character> target) {
Map<Character, Long> targetMap = target.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
for (Character character : source) {
Long count = targetMap.get(character);
if (count != null) {
if (count > 1) {
targetMap.put(character, --count);
} else {
targetMap.remove(character);
}
}
}
return targetMap.isEmpty();
}
Suppose I have a Collection, and a Predicate that matches elements I'd like to remove from the Collection. But I don't just want to discard them, I want to move the matched elements into a new collection. I'd do something like this in Java 7:
List<E> removed = new LinkedList<>();
for (Iterator<E> i = data.iterator(); i.hasNext();) {
E e = i.next();
if (predicate.test(e)) {
removed.add(e);
i.remove();
}
}
I'm curious if there's a streams / Java 8 way to do it. Collections.removeIf() unfortunately simply returns a boolean (not even a count of the number of removed elements? Too bad.) I envision something like this (though of course .removeAndYield(Predicate) doesn't exist):
List<E> removed = data.removeAndYield(predicate).collect(Collectors.toList());
Note: this question was inspired by a similar question; this question is about the more general case of getting a stream over the items removed from a collection. As pointed out in the linked question, the imperative solution may be more readable, but I'm curious if this is even possible with streams.
Edit: Clearly we can split the task into two separate steps, and assuming the appropriate data structures it will be efficient. The question is can this be done on arbitrary collections (which may not have efficient .contains() etc.).
If you don't mind, let me bend your requirements a little bit. :-)
One characteristic of the desired result is that the matching elements should end up in one collection, and the non-matching elements should end up in a different collection. In the pre-Java-8 mutative world, the easiest way to think about getting a collection of non-matching elements is to remove the matching elements from the original collection.
But is removal -- modification of the original list -- an intrinsic part of the requirement?
If it isn't, then the result can be achieved via a simple partitioning operation:
Map<Boolean, List<E>> map = data.stream().collect(partitioningBy(predicate));
The result map is essentially two lists, which contain the matching (key = true) and non-matching (key = false) elements.
The advantage is that this technique can be done in one pass and in parallel if necessary. Of course, this creates a duplicate list of non-matching elements compared to removing the matches from the original, but this is the price to pay for immutability. The tradeoffs might be worth it.
I'd keep it simple:
Set<E> removed = set.stream()
.filter(predicate)
.collect(Collectors.toSet());
set.removeAll(removed);
If you want a functional way to do this, you could write your own method.
static <E> Set<E> removeIf(Collection<? extends E> collection, Predicate<? super E> predicate) {
Set<E> removed = new HashSet<>();
for (Iterator<? extends E> i = collection.iterator(); i.hasNext();) {
E e = i.next();
if (predicate.test(e)) {
removed.add(e);
i.remove();
}
}
return removed;
}
This could be used to remove all odd numbers from a List.
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Set<Integer> removed = removeIf(set, i -> i % 2 != 0);
System.out.println(set);
System.out.println(removed);
Just write yourself a reusable function like this:
/**
* Removes all matching element from iterator
*
* #param it
* #param predicate
*/
public static <E> void removeMatching(final Iterator<E> it, final Predicate<E> predicate) {
while (it.hasNext()) {
final E e = it.next();
if (predicate.test(e)) {
it.remove();
}
}
}
I have also not found a pre-existing solution for Streams. Using iterator.remove() requires less memory than a temporary set.
Hi i have two Collection of SomeType a1,a2 and want to remove all the elements of a2 from a1.
Please suggestion which type of Collection i need to use :
ArrayList
LinkList
some other ?.
Is there any library for this ?
Thanks to all.
After reading your response i created a Filter class like this :
public class Filter {
public <T> Set<T> filter(Set<T> all, Set<T> blocked) {
for (T t : all) {
if(blocked.contains(t)) {
all.remove(t);
}
}
return all;
}
}
Use the collection method Collection.removeAll(Collection<?> c);
Well, you can use a1.removeAll(a2), but the removal would be more efficient if your Collections are HashSet (since the search for an element in a HashSet takes O(1), while in Lists it takes O(n)). Whether you can use HashSet depends on whether a1 and a2 can contain duplicate elements.
To remove from a collection you need to have objects(in your case SomeType) that override equals and hashCode.
Then you don't need a library, just use the removeAll method
Collection<SomeType> a1 = new ArrayList<SomeType>();
Collection<SomeType> a2 = new ArrayList<SomeType>();
a1.removeAll(a2);
while developing I was trying to return an empty List.
public Collection<?> getElements() {
// return elements
}
I searched for an easy way, my first idea was to create for example an ArrayList without any elements and return it. Like the following example:
public Collection<?> getElements() {
return new ArrayList<?>();
}
For me it is too much overhead for an empty list.
There is a really simple solution for the above described "problem":
public Collection<?> getElements() {
return Collections.EMPTY_LIST;
}
That returns an empty list.
Notice:
It returns an immutable object! You can use it only, if you need an object, which isn't editable.
Type-safety
In the case you want to get a type-safe list you should use the following example [1]:
List<String> s = Collections.emptyList();
Three kinds of interfaces are supported:
List:
List l = Collections.EMPTY_LIST;
List<String> s = Collections.emptyList();
Map:
Map m = Collections.EMPTY_MAP;
Map<String> ms = Collections.emptyMap();
Set:
Set s = Collections.EMPTY_SET;
Set<String> ss = Collections.emptySet();
Notice:
Implementations of this method need not create a separate XXX object
for each call. Using this method is likely to have comparable cost to
using the like-named field. (Unlike this method, the field does not
provide type safety.)