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.
Related
I have a collection of custom structures in Java:
private List<CustomStructure> items = new ArrayList<>();
I also have a method, that when called on a single object CustomStructure - returns the value of one of the fields. This method is called .getDirectName().
I need a method that goes through my list items and puts on the first index the element which direct name equals to some input String.
So if the list contains the following elements:
first
|
--> getDirectName() -> one
second
|
--> getDirectName() -> two
third
|
--> getDirectName() -> three
fourth
|
--> getDirectName() -> four
and the method gets three as an input param, the result should be:
third first second fourth
What's the most efficient way to do it in java 8?
If there might be multiple occurrences of the item or you just like fancy Java 8 features, you can use
list.sort(Comparator.comparing(itemToBeMovedToFront::equals).reversed());
(without reversed(), it would move them to the end).
However, if there can be at most one occurrence, the Java 2 variant
Collections.rotate(list.subList(0, list.indexOf(itemToBeMovedToFront)+1), 1);
might be more efficient.
Adapting them to use a property of the list element yields
list.sort(Comparator.comparing(
(CustomStructure cs) -> cs.getDirectName().equals(dirNameToBeMovedToFront)).reversed());
for the first variant
or
IntStream.range(0, list.size())
.filter(ix -> list.get(ix).getDirectName().equals(dirNameToBeMovedToFront))
.findAny()
.ifPresent(index -> Collections.rotate(list.subList(0, index+1), 1));
for the second. Now, even the second variant uses Java 8 features, as finding an element with a certain property was not that neat before Java 8…
public static void relocateToTop(List<CustomStructure> items, String directName) {
for (Iterator<CustomStructure> iterator = items.iterator(); iterator.hasNext(); ) {
CustomStructure item = iterator.next();
if(Objects.equals(item.getDirectName(), directName)) {
iterator.remove();
items.add(0, item);
return;
}
}
}
[EDIT]
Remember to always use Iterator to iterate through your List even when you are going to remove one of them because in case your list is of type of LinkedList iterating using a regular for and getting items by their index makes order of your algorithm O(n^2). For example something like this:
for(int i = 0; i < list.size(); i++)
System.out.println(list.get(i));
If the list is a LinkedList then in every calling of get method we have another loop from the head of the LinkedList to reach the i-th element because unlike ArrayList, LinkedList does not store elements by their index.
If you're using java 8 (as implied), you always have the Stream::filter method to find your things.
final List<CustomStructur> filtered = items.stream()
.filter(item -> givenName.equals(item.getDirectName()))
.collect(Collectors.toList());
items.removeAll(filtered);
items.addAll(0, filtered);
I have a set like below:
HashSet<Integer> set = new HashSet<Integer>();
set.add(1);
How can I get the 1 out? I can do it by for(integer i : set). My specified problem is "Given an array of integers, every element appears twice except for one. Find that single one."
I want to use add elements into the set if the set doesn't contain it and remove existing elements during the loop. And the last remaining element is the answer. I don't know how to return it.
public static int singleNumber(int[] A) {
HashSet<Integer> set = new HashSet<>();
for (int a : A) {
if (!set.contains(a)) {
set.add(a);
} else {
set.remove(a);
}
}
/**
* for(Integer i : set) { return i; }
*return A[0]; //need one useless return
/**
* while(set.iterator().hasNext()) { return set.iterator().next(); }
* return A[0]; //need one useless return
*/
return set.toArray(new Integer[1])[0];
}
set.iterator().next()
Do so only if you are sure there is an element in the set. Otherwise next() will throw an exception.
Simply try using HashSet#toArray() method
HashSet<Integer> set = new HashSet<Integer>();
set.add(1);
if (set.size() == 1) { // make sure that there is only one element in set
Integer value = set.toArray(new Integer[1])[0];
System.out.println(value);//output 1
}
The typical solution includes a check if the current iterator position has any left element through setIterator.hasNext() which returns true only if there is an extra element unchecked. For example
HashSet set = new HashSet();
Iterator setIterator = set.iterator();
while(setIterator.hasNext()){
String item = setIterator().next();
...
}
If you know what the element is and you just want to empty the set, you can use remove(Object o) (or clear() for that matter). If you don't know what it is and want to see it without removing it, use an iterator. If you don't know what it is and want to remove it, you should use an iterator using the iterator's remove() method. This is the safe (and recommended) way to remove elements from a collection. Since Set is unordered, it's difficult to specify what you want to remove. And for any collection, you would usually only know what you are removing if you iterate through the collection.
Solution Using Java Stream
If you want to use Java Stream any of the following options will give you the desired result:
Option 1
Just return the only element from the stream:
set.stream().findFirst().get()
Option 2
Or use Stream.reduce; since adding the only element to zero has no effect
(a little too much IMHO :))
set.stream().reduce(0, Integer::sum)
You can test it here.
2nd question, which is continue of first.
I have got two Lists of strings. There is an List of strings (asu) - M1, M2, M3 ... As well as an List of string (rzs) - M1, M2, M3 and all possible combinations thereof. The need for each element (asu) (for example M1) to find an element in (rzs) (M1, M1M2, ..), which contains (e.g. M1). Example: took M1 from (asu) and will start search for duplicate(contain) in (rzs). We found M1M2 in (rzs), it contains M1. After that we should delete both elements from lists. Great thanks to No Idea For Name helped for modification this code. But the program always fails because AbstractList.remove error. Please help to implementation logic and tuning code!
Imports..........
public class work{
List<string> asu = Arrays.asList("M1","M1","M1","M3","M4","M5","M1","M1","M1","M4","M5","M5");
List<string> rzs = Arrays.asList("M1","M2","M3","M4","M5",
"M1M2","M1M3","M1M4","M1M5","M2M3","M2M4","M2M5","M3M4","M3M5","M4M5"
,"M1M2M3","M1M2M4","M1M2M5","M1M3M4","M1M3M4","M1M4M5","M2M4","M2M5");
public static void main(String[] args) {
work bebebe = new work();
bebebe.mywork();
}
List<string> tmp1 = new ArrayList<string>();
List<string> tmp2 = new ArrayList<string>();
System.out.println(Arrays.deepToString(rzs));
System.out.println(Arrays.deepToString(asu));
for (string curr : asu){
for (string currRzs : rzs){
System.out.println("New iteration ");
if (currRzs.contains(curr)) {
System.out.println("Element ("+curr+") in ASU =
element ("+currRzs+") in RZS");
if(tmp1.contains(curr) == false)
tmp1.add(curr);
if(tmp2.contains(currRzs) == false)
tmp2.add(currRzs);
}
}
}
for (string curr : tmp1){
asu.remove(curr);
}
for (string currRzs : tmp2){
rzs.remove(currRzs);
}
You should try to make use of removeAll() or retainAll() methods of Collection.
For example:
List<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("b");
aList.add("c");
aList.add("d");
aList.add("e");
List<String> bList = new ArrayList<String>();
bList.add("b");
bList.add("e");
bList.add("d");
aList.removeAll(bList);
will give you the "a" and "c" elements left in aList
While if you try to make use of retainAll() method:
aList.retainAll(bList);
will give you "b", "d" and "e" elements left in aList;
retainAll() is used to remove all the elements of the invoking collection which are not part of the given collection.
removeAll() is used to remove all the elements of a collection from another collection.
So, it all depends on your use-case.
EDIT
If in any case you want to remove some elements from these collections while iterating conditionally then you should first obtain the Iterator<Type> then call the remove() method over it.
Like:
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals('test')){
iterator.remove();
}
}
Don't remove items from list using foreach loop. Use classic for and iterate over elements, and when removing item, decrease iterator.
To safely remove elements while iterating use Iterator.remove method:
The behavior of an iterator is unspecified if the underlying
collection is modified while the iteration is in progress in any way
other than by calling this method.
Iterator<String> i = tmp1.iterator();
while (i.hasNext()) {
i.next(); // must be called before remove
i.remove();
}
Also it is easier to remove all collection from another by simply calling:
asu.removeAll(tmp1);
instead of List you can use Set, which will remove automatically the duplicate elements...
You can use removeAll() method to remove collection of elements from the list instead of removing one by one.
use
asu.removeAll(tmp1);
instead of
for (string curr : tmp1)
{
asu.remove(curr);
}
and use
rzs.removeAll(tmp2);
instead of
for (string currRzs : tmp2)
{
rzs.remove(currRzs);
}
update
I trace out your problem.The problem lies in Arrays.asList() method.
According to Arrays#asList
asList() returns "a fixed-size list backed by the specified array". If you want to resize the array, you have to create a new one and copy the old data. Then the list won't be backed by the same array instance.
So create a duplicate ArrayList for the lists.Like this
List<string> asuDuplicat = new ArrayList<string>(asu);
List<string> rzsDuplicat = new ArrayList<string>(rzs);
use asuDuplicat,rzsDuplicat.
asuDuplicat.removeAll(tmp1);
rzsDuplicat.removeAll(tmp2);
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]]
lets say I have an List. There is no problem to modify list's item in for loop:
for (int i = 0; i < list.size(); i++) { list.get(i).setId(i); }
But I have a SortedSet instead of list. How can I do the same with it?
Thank you
First of all, Set assumes that its elements are immutable (actually, mutable elements are permitted, but they must adhere to a very specific contract, which I doubt your class does).
This means that generally you can't modify a set element in-place like you're doing with the list.
The two basic operations that a Set supports are the addition and removal of elements. A modification can be thought of as a removal of the old element followed by the addition of the new one:
You can take care of the removals while you're iterating, by using Iterator.remove();
You could accumulate the additions in a separate container and call Set.addAll() at the end.
You cannot modify set's key, because it causes the set rehasing/reordering. So, it will be undefined behaviour how the iteration will run further.
You could remove elements using iterator.remove(). But you cannot add elements, usually better solution is to accumulate them in a new collection and addAll it after the iteration.
Set mySet = ...;
ArrayList newElems = new ArrayList();
for(final Iterator it = mySet.iterator(); it.hasNext(); )
{
Object elem = it.next();
if(...)
newElems.add(...);
else if(...)
it.remove();
...
}
mySet.addAll(newElems);
Since Java 1.6, you're able to use a NavigableSet.
You should use an Iterator or better still the enhanced for-loop syntax (which depends on the class implementing the Iterable interface), irrespective of the Collection you're using. This abstracts away the mechanism used to traverse the collection and allows a new implementation to be substituted in without affecting the iteration routine.
For example:
Set<Foo> set = ...
// Enhanced for-loop syntax
for (Foo foo : set) {
// ...
}
// Iterator approach
Iterator it = set.iterator();
while (it.hasNext()) {
Foo foo = it.next();
}
EDIT
Kan makes a good point regarding modifying the item's key. Assuming that your class's equals() and hashCode() methods are based solely on the "id" attribute (which you're changing) the safest approach would be to explicitly remove these from the Set as you iterate and add them to an "output" Set; e.g.
SortedSet<Foo> input = ...
SortedSet<Foo> output = new TreeSet<Foo>();
Iterator<Foo> it = input.iterator();
while (it.hasNext()) {
Foo foo = it.next();
it.remove(); // Remove from input set before updating ID.
foo.setId(1);
output.add(foo); // Add to output set.
}
You cannot do that. But you may try, maybe you'll succeed, maybe you'll get ConcurrentModificationException. It's very important to remember, that modifying elements while iterating may have unexpected results. You should instead collect that elements in some collection. And after the iteration modify them one by one.
This will only work, if id is not used for equals, or the comperator you used for the sorted set:
int counter = 0;
for(ElementFoo e : set) {
e.setId(counter);
couter++;
}