I have a TreeSet in Java and I have my own comparator function for this tree set. Now I am traversing this tree set using descendingIterator() method and changing the elements. So does this update the actual tree set as well wrt to the way it is sorted with my custom comparator? Or do I need to remove the element and put back the updated element?
You need to remove the element and add it back. The position of the element in the tree is decided when the element is inserted, by comparing it with other elements. If you change the object so that the comparison to other elements changes, you must remove the element first, then change it, then re-add it.
Note that removing the element while iterating will only work using the iterator's remove method. And you won't be able to add it during the iteration without getting a ConcurrentModificationException, AFAIK. So store it in a list of elements to be re-added to the set once the iteration has ended.
If you modify any part of the object that is a part of the "key" (as defined by your custom comparator) you need to remove and re-insert the object for the tree set to "learn" about the change. You should not be doing it while you are iterating, either: a good approach is to collect items that need changing in one loop, and then modify and re-insert them in another loop.
As a general rule of thumb, it isn't advisable to "modify" any value types added to Java containers which rely on equality, hash code etc. given that none of the known standard containers perform auto-balancing or adjustment in response to the change of values (which makes sense).
Along with Set, this rule is equally valid for Map types. If you are iterating over a map and modify the "key" in-place, things go bad. This is the reason why it is recommended to have immutable types as your Map keys (think of String, Integer etc.) Your case can be demonstrated by a simple example:
public class Test {
public static void main(final String[] args) {
Mutable m1 = new Mutable(1);
Mutable m2 = new Mutable(2);
Mutable m3 = new Mutable(3);
Mutable m4 = new Mutable(4);
TreeSet<Mutable> ts = new TreeSet<Mutable>(new Cmp());
ts.add(m1); ts.add(m2); ts.add(m3); ts.add(m4);
System.out.println(ts);
for (Iterator<Mutable> iter = ts.iterator(); iter.hasNext(); ) {
Mutable m = iter.next();
if (m.i == 1 || m.i == 3) {
m.i = m.i + 10;
}
}
System.out.println(ts);
}
}
class Mutable {
public int i;
public Mutable(int i) {
this.i = i;
}
public String toString() {
return "Mutable[" + i + "]";
}
}
class Cmp implements Comparator<Mutable> {
#Override public int compare(Mutable o1, Mutable o2) {
return Integer.valueOf(o1.i).compareTo(Integer.valueOf(o2.i));
}
}
Output:
[Mutable[1], Mutable[2], Mutable[3], Mutable[4]]
[Mutable[11], Mutable[2], Mutable[13], Mutable[4]]
Related
I need some how to modify set during iteration by retainAll
Let's say I have next code:
set.forEach(it - > {
set.retainAll(someMapWithSets.get(it))
});
I've tried approach with iterators:
for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
String value = iterator.next();
set.retainAll(someMapWithSets.get(value))
}
It did not work. ConcurrentModificationException was thrown.
How to do that correctly?
Update.
Whole task is the next:
I have set
{A,B,C}
And I have map of sets which indicates if elements are compatible:
A: B,C - (means A is compatible with B and C and etc)
B: A,E,C
C: A,B
I need to have map of sets where all possible combinations of compatible elements are present:
A,B,C
So as I part of solution I thought I could create method that retains only compatible elements for the given set and chosen element.
public static Set<String> define(Set<String> elements, String rootElement) {
Set<String> result = someMapWithElements.get(rootElement);
result.retainAll(elements);
result.add(rootElement);
result.forEach(it -> result.retainAll(someMapWithElements.get(it)))
return result;
}
But obviously I get ConcurrentModificationException
You cannot change a set using the retainAll method while iterating over that same object. Therefore you will need to create a new set (or other collection) to use as an iteration variable, like:
new HashSet(set).forEach(it - > {
set.retainAll(someMapWithSets.get(it))
});
HashSet can be replaced by TreeSet or any other set object that is suitable.
If actually need to change the set while iterating, you will need to use the remove method in Iterator.
The trick is that this object MediaContainerModel inherits equals(Object) directly from Object and I can't and don't want to override equals in its class definition. This is what I have at the moment:
private void addMediaContainer(MediaContainerModel newMediaContainer, ProductModel product) {
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
MediaContainerModel inserted = null;
// if a MediaContainer with same qualifier as newMediaContainer already exists, replace it
// with the new one to prevent duplicates
for (int i = 0; i < galleryImages.size(); i++) {
if (galleryImages.get(i).getQualifier().equals(newMediaContainer.getQualifier())) {
inserted = galleryImages.set(i, newMediaContainer);
}
}
// if not, add it
if (inserted == null) {
galleryImages.add(newMediaContainer);
galleryImages.sort((image1, image2) -> image2.getQualifier().compareTo(image1.getQualifier()));
}
product.setGalleryImages(galleryImages);
}
I want to do the same thing without the ugly for-loop by overriding MediaContainerModel.equals(Object) for this method only so I can use List.indexOf(Object) or something with lambdas. Is this possible in Java? If so how? Thanks!
without using a loop
I bet you are looking for the java-stream way:
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
galleryImages.stream()
.filter(image -> newMediaContainer.getQualifier() // filter the equal ones
.equals(image.getQualifier()))
.findAny() // find any existing
.ifPresent(image -> { // add if present
galleryImages.add(newMediaContainer);
galleryImages.sort(Comparator.comparing(MediaContainerModel::getQualifier));
});
product.setGalleryImages(galleryImages);
Few notes:
The filtering uses exhaustive iteration as well as for-loop which means that all elements are iterated and multiple equal MediaContainerModel objects with same qualifiers. That's fine as long as you want to find if there is any qualified (findAny). Otherwise, to find the last one, you have to replace the line with:
.reduce((first, second) -> second)
The result using Java Stream API is a bit clumsy. I see you insert a new element and sort the list which means your intention is to keep the list always sorted. If there are no duplicate values allowed, I recommend using rather TreeSet which keeps the elements sorted upon addition or deletion. The whole solution would be the way easier:
Set<MediaContainerModel> galleryImages = new TreeSet<>(Comparator.comparing(MediaContainerModel::getQualifier));
galleryImages.addAll(product.getGalleryImages());
galleryImages.add(newMediaContainer); // won't be added if is already present
product.setGalleryImages(new ArrayList<>(galleryImages));
... if the ProductModel uses Collection or Set over List, then the last line is more straightforward:
product.setGalleryImages(galleryImages);
Is there a Guava Iterator (or methodology) for List objects which allows two iterator instances to exist - in the same memory scope - while allowing the remove() operation? (Bonus point: if it works for a Collection).
Example use case: an outer and an inner iteration through a collection, where the inner loop might decide to remove an element and the outer loop would subsequently skip it.
Imagine how it would benefit the following concept code (which uses Guava static imports) by reducing the number of elements to compare in the loop and also removing the need to remove empty sets from the list at the end:
private <T> Set<Set<T>> disjointify(Collection<Set<T>> sets) {
List<Set<T>> disjoint = newArrayList(sets);
for (Set<T> set1 : disjoint) {
for (Set<T> set2 : filter(disjoint, not(equalTo(set1)))) {
if (!intersection(set1, set2).isEmpty()) {
// this wouldn't be safe for a Set<Set<T>>
set1.addAll(set2);
set2.clear();
}
}
}
return newHashSet(filter(disjoint, NO_EMPTIES));
}
private static final Predicate<Set<?>> NO_EMPTIES = new Predicate<Set<?>>() {
#Override
public boolean apply(Set<?> input) {
if (input == null || input.isEmpty()) {
return false;
}
return true;
}
};
Note: one can easily imagine creating the implementation - especially for LinkedList - I'm just asking if one already exists here.
For the record, if an efficient Iterable did already exist, and worked for Sets, then the use case would look like the following (I have created my own very inefficient Iterable which achieves this, but it is 50 lines long and ludicrously inefficient - so I use the original code above):
private <T> void disjointify(Set<Set<T>> sets) {
for (Set<T> set1 : nestable(sets)) {
Iterator<Set<T>> it = filter(nestable(sets), not(equalTo(set1))).iterator();
while (it.hasNext()) {
Set<T> set2 = it.next();
if (!intersection(set1, set2).isEmpty()) {
set1.addAll(set2);
it.remove();
}
}
}
}
It would appear that such an implementation does not exist in standard libraries.
Why not just filter the outer look with NO_EMPTIES? Since filter is evaluated during iteration, any newly empty sets would not be returned in the filtered list / outer loop.
Otherwise, I don't think so. You would get a ConcurrentModificationException on the outter loop.
I am storing Integer objects representing an index of objects I want to track. Later in my code I want to check to see if a particular object's index corresponds to one of those Integers I stored earlier. I am doing this by creating an ArrayList and creating a new Integer from the index of a for loop:
ArrayList<Integer> courseselectItems = new ArrayList();
//Find the course elements that are within a courseselect element and add their indicies to the ArrayList
for(int i=0; i<numberElementsInNodeList; i++) {
if (nodeList.item(i).getParentNode().getNodeName().equals("courseselect")) {
courseselectItems.add(new Integer(i));
}
}
I then want to check later if the ArrayList contains a particular index:
//Cycle through the namedNodeMap array to find each of the course codes
for(int i=0; i<numberElementsInNodeList; i++) {
if(!courseselectItems.contains(new Integer(i))) {
//Do Stuff
}
}
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I hope I didn't make this too unclear. Thanks for the help!
Yes, you can use List.contains() as that uses equals() and an Integer supports that when comparing to other Integers.
Also, because of auto-boxing you can simply write:
List<Integer> list = new ArrayList<Integer>();
...
if (list.contains(37)) { // auto-boxed to Integer
...
}
It's worth mentioning that:
List list = new ArrayList();
list.add(new Integer(37));
if (list.contains(new Long(37)) {
...
}
will always return false because an Integer is not a Long. This trips up most people at some point.
Lastly, try and make your variables that are Java Collections of the interface type not the concrete type so:
List<Integer> courseselectItems = new ArrayList();
not
ArrayList<Integer> courseselectItems = new ArrayList();
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
The short answer is yes.
The long answer is ...
That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I assume you mean "... will that be the same instance as ..."? The answer to that is no - calling new will always create a distinct instance separate from the previous instance, even if the constructor parameters are identical.
However, despite having separate identity, these two objects will have equivalent value, i.e. calling .equals() between them will return true.
Collection.contains()
It turns out that having separate instances of equivalent value (.equals() returns true) is okay. The .contains() method is in the Collection interface. The Javadoc description for .contains() says:
http://java.sun.com/javase/6/docs/api/java/util/Collection.html#contains(java.lang.Object)
boolean contains(Object o)
Returns true if this collection
contains the specified element. More
formally, returns true if and only if
this collection contains at least one
element e such that (o==null ? e==null
: o.equals(e)).
Thus, it will do what you want.
Data Structure
You should also consider whether you have the right data structure.
Is the list solely about containment? is the order important? Do you care about duplicates? Since a list is order, using a list can imply that your code cares about ordering. Or that you need to maintain duplicates in the data structure.
However, if order is not important, if you don't want or won't have duplicates, and if you really only use this data structure to test whether contains a specific value, then you might want to consider whether you should be using a Set instead.
Short answer is yes, you should be able to do ArrayList.contains(new Integer(14)), for example, to see if 14 is in the list. The reason is that Integer overrides the equals method to compare itself correctly against other instances with the same value.
Yes it will, because List.contains() use the equals() method of the object to be compared. And Integer.equals() does compare the integer value.
As cletus and DJ mentioned, your approach will work.
I don't know the context of your code, but if you don't care about the particular indices, consider the following style also:
List<Node> courseSelectNodes = new ArrayList<Node>();
//Find the course elements that are within a courseselect element
//and add them to the ArrayList
for(Node node : numberElementsInNodeList) {
if (node.getParentNode().getNodeName().equals("courseselect")) {
courseSelectNodes.add(node);
}
}
// Do stuff with courseSelectNodes
for(Node node : courseSelectNodes) {
//Do Stuff
}
I'm putting my answer in the form of a (passing) test, as an example of how you might research this yourself. Not to discourage you from using SO - it's great - just to try to promote characterization tests.
import java.util.ArrayList;
import junit.framework.TestCase;
public class ContainsTest extends TestCase {
public void testContains() throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
assertFalse(list.contains(new Integer(17)));
list.add(new Integer(17));
assertTrue(list.contains(new Integer(17)));
}
}
Yes, automatic boxing occurs but this results in a performance penalty. Its not clear from your example why you would want to solve the problem in this manner.
Also, because of boxing, creating the Integer class by hand is superfluous.
Given a list of objects (all of the same type), how can I make sure that it contains only one element for each value of a certain attribute, even though equals() may return false for such elements due to more attributes being checked? In code:
private void example() {
List<SomeType> listWithDuplicates = new ArrayList<SomeType>();
/*
* create the "duplicate" objects. Note that both attributes passed to
* the constructor are used in equals(), though for the purpose of this
* question they are considered equal if the first argument was equal
*/
SomeType someObject1 = new SomeObject1("hello", "1");
SomeType someObject2 = new SomeObject1("hello", "2");
List<SomeType> listWithoutDuplicates = removeDuplicates(listWithDuplicates)
//listWithoutDuplicates should not contain someObject2
}
private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
/*
* remove all but the first entry in the list where the first constructor-
* arg was the same
*/
}
Could use a Set as an intermediary placeholder to find the duplicates as Bozho suggested. Here's a sample removeDuplicates() implementation.
private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
/* Set of all attributes seen so far */
Set<AttributeType> attributes = new HashSet<AttributeType>();
/* All confirmed duplicates go in here */
List duplicates = new ArrayList<SomeType>();
for(SomeType x : listWithDuplicates) {
if(attributes.contains(x.firstAttribute())) {
duplicates.add(x);
}
attributes.add(x.firstAttribute());
}
/* Clean list without any dups */
return listWithDuplicates.removeAll(duplicates);
}
Maybe a HashMap can be used like this:
private List<SomeType> removeDuplicates(List<SomeType> listWithDuplicates) {
/*
* remove all but the first entry in the list where the first constructor-
* arg was the same
*/
Iterator<SomeType> iter = listWithDuplicates.iterator();
Map<String, SomeType> map = new HashMap<String, SomeType>();
while(iter.hasnext()){
SomeType i = iter.next();
if(!map.containsKey(i.getAttribute())){
map.put(i.getAttribute(), i);
}
}
//At this point the map.values() is a collection of objects that are not duplicates.
}
If equals() were suitable, I could recommend some "standard" Collections classes/methods. As it is, I think your only option will be to either
copy each element to another list after first checking all preceding elements in the original list for duplicates; or
delete from your list any element for which you've found a duplicate at a preceding location. For in-list deletion, you'd be best off with using a LinkedList, where deletion isn't so expensive.
In either case, checking for duplicates will be an O(n^2) operation, alas.
If you're going to be a lot of this kind of operation, it might be worthwhile to wrap your list elements inside another class that returns a hashcode based on your own defined criteria.
I'd look at implementing the Comparator interface for something like this. If there's a simple attribute or two that you wish to use for your comparison, that makes it pretty straightforward.
Related question: How Best to Compare Two Collections in Java and Act on Them?