How to overwrite a HashSet element in Java while iterating over it - java

Is it possible to overwrite some HashSet element (not necessarily the one iterated) while iterating over it. I want to know of a way apart from removing, editing and then re-adding it?
The reason I am asking this is because using remove while iterating always gives the java.util.ConcurrentModificationException. I figured out that there is no method to overwrite a set element.
Any help will be appreciated.
Thanks,
Somnath

You can remove an item from a Collection - including a Set - whilst iterating over it as long as you iterate over it using an Iterator and call Itertator.remove() to do the remove. However, this will only allow you to remove the current item in the iteration.
You can't remove another item or add one as this will cause a ConcurrentModificationException, since if you change the Set it's not clear how you would iterate over it.
Also, unlike a List, is doesn't quite make sense to talk about replacing an entry in a Set. In a List items have a define order so, for example, if the second entry was "A" you could replace it with "B". Items in a Set don't have an order so you can't replace one with another directly, all you can do is remove the old one and add the new one.
Depending quite on what you want to do, your best approach might be to loop over a copy of the Set:
Set<Object> originalSet = someMethod();
for (Object item : new HashSet<Object>(originalSet)) {
//do stuff which modifies originalSet
}
However, you'd have to account for the fact that the objects you iterated over would be the original values and wouldn't reflect any changes you'd made.
If this won't do, then it might make sense to find another way of process the items in the Set without simply iterating over them by keeping track of which nodes you've processed.
Something like this might do but could probably be improved depending on what you're doing:
Set<Object> originalSet = somemethod();
//Keep track of items we've already processed
Set<Object> processed = new HashSet<Object>();
//toDo is used to calculate which items in originalSet aren't in processed
Set<Object> toDo = new HashSet(originalSet);
toDo.removeAll(processed);
while (! toDo.isEmpty()) {
//Get an object from toDo
Object item = toDo.iterator().next();
//Do some processing
//Can update originalSet or even remove from processed if we want to re-do an item
//Recalculate what's left to do
processed.add(item);
toDo = new HashSet(originalSet);
toDo.removeAll(processed);
}

Set<Key> set = Sets.newHashSet();
// ...
for (Key k : ImmutableSet.copyOf(set)) {
if (needToMessWith(k)) {
set.remove(k);
k.mutateAsNeeded();
set.add(k);
}
}
// I used the guava collections code. It is simple enough to do without.

Just convert set to List and use set() and once you are done with this convert it back to Set
or maintain another Set while iterating

There is no way to do that. Because replacing an element is basically removing + inserting.
This is a set, not a list. For example, there are two elements in a set {1, 2} and you want to replace {1} with {2}. This is not possible as the result set cannot contain {2, 2} - i.e. two elements which are equal.

Related

using Enumeration<E> 's hasMoreElements() and nextElement() method to print a list, succeeded 1st time, failed 2nd time after adding some element

I'm new to Java and is trying to learn the Enumeration 's hasMoreElements() and nextElement() method. After I added some elements to the p1 list while deleted others from the p1 list, I tried to print everything, however, it failed to do so? Why is this happening?
Properties p1 = new Properties();
try (OutputStream os1 = new FileOutputStream("xanadu123.properties")) {
//set the properties value
p1.setProperty("database", "localhost");
p1.setProperty("1", "one");
p1.setProperty("2", "two");
} catch (IOException e) {
e.printStackTrace();
}
Enumeration<Object> eString1 = p1.elements();
while (eString1.hasMoreElements()) {
System.out.println(eString1.nextElement());
}
System.out.println("after printing eString1");
p1.setProperty("3", "three");
p1.remove("2");
System.out.println("before printing eString1 again");
while (eString1.hasMoreElements()) {
System.out.println(eString1.nextElement());
}
Output:
two
one
localhost
after printing eString1
before printing eString1 again
The basic answer is that you cannot rely on Enumeration working if you modify the thing you're enumerating (in this case, the Properties). Once you do that, the Enumeration you constructed may no longer work.
The newer version of Enumeration, Iterator, is the same way, with the exception that some Iterators provide a remove method that allows you to remove the "current element" from a collection without breaking the iterator. (But you have to use the remove method of the Iterator; you can't use any other mechanism for removing an element, or else you could break the iterator.)
The basic reason is that an Enumeration or Iterator contains state information about the entity you're enumerating. If you modify that entity, the state information may no longer be valid. Suppose, for example, that you're iterating over an ArrayList. Most likely, the iterator will contain an integer that's the "current" index of the array. If you then insert something at the front of the ArrayList, the element that is pointed to by that index will become a different element, and next time you call next on the iterator, it will look at the element at that index and return the same element it returned last time. In your case, I'd guess that one piece of state information kept in the Enumeration is a boolean instance variable that says, "Have I reached the end yet?" Once you reach the end of the Properties, that variable will become true. If you then add more properties, the "end-of-the-enumeration" variable will still be true, even though it's effectively wrong at this point.
It simply isn't practical to try to set up Enumerators and Iterators so that they adjust themselves whenever the underlying entity changes. Either a Properties object (or an ArrayList or whatever) would need to keep track of every Enumerator or Iterator created from it, and then go find all of them and adjust them whenever something is done that changes the Properties; or else each Enumerator or Iterator would need a way to query the Properties to ask it, what changes have happened since the last time I checked? Either of those would be very complicated. Consider also that Properties is a hash table, so that when you add a new element, you could add it to any bucket in the hash table. If the Enumerator has already scanned over that bucket, how would it know to go back and scan it again, after you've added a new element? An Enumerator that works that way could be written, I believe, but it would be very inefficient and messy and complicated.
there are two things to notice here:
elements() creates the enumeration from properties. once it's created, it is disconnected from the properties itself. The meaning of that is that adding one more property afterwards will not be added to the enumeration
an enumeration is a 'one time' object, similar to iterator. This means you can loop over the elements one time, after that, the hasMoreElements will return false every time you call it.
given that two facts, it makes perfect sense the second print prints nothing
Do not forget to reset your enumeration before using it again.
eString1 = p1.elements();
while (eString1.hasMoreElements()) {
System.out.println(eString1.nextElement());
}

Remove an array of ids contained in another array

If I have an array of Track objects:
Track[] tracks;
Where each track has many fields among others a String trackId:
public class Track{
private String trackId;
private String artistId;
}
then I have
String trackIdsToRemove[];
I would like to remove the ids in trackIdsToRemove[] that are in the objects inside the list tracks.
Is there any fancy way to do this other than iterate? Maybe with Guava? Or RxJava?
If you are using java 8 and Guava:
Set<String> toRemove = Sets.newHashSet(trackIdsToRemove);
tracks = Arrays.stream(tracks)
.filter(t -> !toRemove.contains(t.trackId))
.toArray(Track[]::new);
No matter what trick / library you will use, the backend will be the same everytime, you need to locate the element to remove, and then proceed to actually removing it.
But there are some little optimization you could do, first off take the best data structure. If you're going to add/remove tracks often maybe you shoud use a list instead of an array.
And then you could also insert your track in an ordered way so you can do a binary search when you need to locate the track to remove. The binary search takes O(log(n)) to find a track in the worst case whereas a normal search takes O(n)
usefull link : http://bigocheatsheet.com/#data-structures
You can use an ArrayList:
ArrayList array = new ArrayList();
//add elements to array -> array.add(object1);
ArrayList arrayToRemove = new ArrayList();
//add elements to array to remove -> array.add(object1);
for(Object obj : arrayToRemove){
array.remove(obj);
}
To do this, if you are using your own object you need to override the next object functions equals and hashCode, here is an example on this thread:
ArrayList's custom Contains method
I'm unsure what you mean by "removing" from an array: you simply can't do that, at best you can set to null the cell content or somehow marked it as invalid/free.
If your array is sorted by id, you can binary search the ids that you want to "remove" to have a better performance: supposing that N is the size of the collection and M of the removal collection, then normal iteration is O(n*m), while by binary searching you get down to O(log(n)*m)
In general, even if you had a library, it would do exactly this with those data structures, just behind the scene.
As others have pointed out, if you need to support deletion it's best to use different structures: given you have ID's it suggests that your items are unique, so a Set could be ideal, otherwise a List should do, or a Map Obj -> Int to implement a multi-set.
Supposing you can change your code to use more ideal structures, you could do something like (Java8):
Set<Track> tracks;
Set<String> idsToRemove;
//Note: this has O(n*m) performance, though you could try using .parallelstream() instead of .stream()
Set<Track> remainingOnes = tracks.stream().filter(x -> !idsToRemove.contains(x.id)).collect(Collectors.toSet());
You cannot remove from array. You can remove from a list. Change your array to list and do that way:
public void remove(String trackIdsToRemove[], List<Track> tracks) {
for(String str: trackIdsToRemove) {
tracks.remove(tracks.indexOf(str));
}
}

How to remove duplicates from a list

I have a list that contain elements, I want to remove the duplicates. I have tried to do that but it didn't work.
This is the code :
//list is the original list containing duplicates
Iterator it1=list.iterator();//iterator1
while (it1.hasNext())
{
Iterator it2=list.iterator(); //iterator2
if(it1.next().equals(it2.next()))
{
//liststeps is the list without duplicates
liststeps.add(it1.next());
}
}
System.out.println("multiple list "+list.toString());
System.out.println("list "+liststeps.toString());
Results:
multiple list [Open, In Progress, Waiting Customer Test, Closed, Open, Step11, Step22, Open, In Progress, Waiting Customer Test, Closed]
list [In Progress, Step11, In Progress]
Use a Set instead of a List. You can use addAll() to add your list to the Set, then convert it back to a List or just use the Set. And if the order is relevant, use a LinkedHashSet.
And just a suggestion: use for-each instead of iterator... it´s easier :-)
Simply construct a HashSet or a TreeSet from your list:
Set<String> s = new HashSet<String>(list);
This will remove the duplicates, but will likely re-order the elements. If you want to preserve the order in which the (unique) elements appear in the original list, use LinkedHashSet:
Set<String> s = new LinkedHashSet<String>(list);
Lastly, if you need the output to be a list, convert the set to a list like so:
List<String> out = new ArrayList<String>(s);
You are calling next way to often, it always moves to the next element.
if(it1.next().equals(it2.next())) {
liststeps.add(it1.next());
}
This code actually adds the next element after the one you compared to your list.
Use a Set/for-each as Tobiask pointed out.

Iteration over a list (ConcurrentModificationException)

The following code throws a ConcurrentModificationException:
for (String word : choices) {
List<String> choicesCopy = choices;
chosen.add(word);
choicesCopy.remove(word);
subsets(choicesCopy, chosen, alreadyPrinted);
}
What's going on? The original list (choices) isn't modified at all.
You made a reference copy not object copy in here
List<String> choicesCopy = choices;
So obviously you are modifying the same list and you are bound to get the ConcurrentModificationException
Use Collections.copy() to properly make a copy of your list.
EDIT:
As suggested below you can also use constructor for copying.
The reason is because you cannot modify anything inside a foreach loop. Try using a for loop. Or you have take all the contents of list and add them 1 at a time to the other list. because its done by reference
Edit: You need to make a deep copy of the list and remove from that copy. Then you can assign the reference of the original list to point to the new one that has the modifications. You cannot remove from the list you're currently iterating through even if it's being referenced by another variable.
Change the code like this:
for (Iterator<String> it = choices.iterator(); it.hasnext();) {
String word = it.next();
chosen.add(word);
it.remove();
subsets(choicesCopy, chosen, alreadyPrinted);
}
Explanation: foreach loops use an iterator internally, but don't expose it to the user. So if you want to remove items you have to simulate the foreach loop and keep a reference to the iterator yourself.
While iterating, any other means of removing data from a collection will result in a ConcurrentModificationException.
I think the universal solution is:
List<E> obj = Collections.synchronizedList(new ArrayList<E>());
You'll need to copy the list properly e.g. Collections.copy and then remove from the copy, or use Iterator.remove, which will remove the Object from the underlying collection. Iterators are fail fast, so you can't change the underlying Collection without using the API of the Iterator.
I suspect chosen should be a copy as well. Otherwise chosen will accumulates all the words by the time the loop has finished. i.e. I suspect the chosen and choices shouldn't have any words in common.
I also suspect the collections should be sets (unordered collections without duplicates) instead of lists.
Perhaps the code should be.
Set<String> choices =
Set<String> chosen =
for (String word : choices) {
Set<String> choicesCopy = new LinkedHashSet<String>(choices);
choicesCopy.remove(word);
Set<String> chosenCopy = new LinkedHashSet<String>(chosen);
chosenCopy.add(word);
subsets(choicesCopy, chosenCopy, alreadyPrinted);
}

Java - How to separate a list based on a property of it's elements

I have a list of objects which I want to perform an operation on. However I firstly need to divide the list into separate lists such that all items with the same parentID are in the same list, and then the operation is performed on each list separately (the reason being that the operation takes the parentID of the objects as a parameter).
What is the best way to separate a list based on a given property of it's elements, as required here? The highest number of objects that will be passed in the original list is < 10,000 and normally will be < 1,000.
All help is much appreciated!
It sounds like you might want to use Multimaps.index from Guava. That will build you a multi-map, where each key has a collection of elements.
The keyFunction passed into index would be a Function which just retrieves the property from a single element.
Create a
Map <IdType, List<YourObject>> map
loop thru the list, and for each id do something like
List theList = map.get(id);
if (theList == null ) {
// create a new list, add it to the map under the id
}
// add the item to theList
then you can loop thru the map's entries and you have a list of objects for each id. This approach does not require you to know how many different ids are in your list to begin with....
I would recommend writing an Iterator that wraps an Iterator, returning only elements that match what you want. You could then write an implementation of Iterable that takes an Iterable, returning such an iterator (this would allow you to use an enhanced for loop).
If you're okay with adding a 3rd party library, Google's Guava supplies various utilities which could help you out.
Specifically, use Collections2.transform like this:
Collection myOriginalList;
Collection mySplitList1 = Collections2.transform(myOriginalList, new Function() { /* method to filter out parent ID 1 */ });
... // repeat for each parent id you're interested in

Categories