How to remove duplicates from a list - java

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.

Related

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 overwrite a HashSet element in Java while iterating over it

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.

Sorted view of a List

is it somehow possible to get a sorted List view of a List with the elements from the original List, modify the elements, for example set a property, but the original List should reflect the changes?
Background is I'm sorting two Lists, then for each equal element I'm setting a property (basically it's the intersection) but finally I need the unsorted List with the modified elements.
kind regards,
Johannes
Probably the simplest thing to do is add the elements to a new list, sort that list, and when you modify the elements, the original elements will still be modified...
List<?> origA;
List<?> origB;
List<?> newA = new ArrayList<?>(origA);
List<?> newB = new ArrayList<?>(origB);
Collections.sort(newA);
Collections.sort(newB);
// do mods
If the List holds references to objects (not primitive data types), then just copy the list, sort it and modify the elements.
Does it have to be a list? If you keep your elements in a TreeSet, they will always be sorted as you iterate through them, even after you add/remove the elements. Remember though that modifying an element already in the TreeSet may break the sort order. You can remove and add the element to the TreeSet to get around that.
If you have to use a list, you can use Collections.sort(List list) after adding or modifying an element. Of course, if you have to call it often, there will be a performance hit. If performance is a concern, you can just insert the new element (or move the modified one) to maintain the sorted order, which will be cheaper than sorting it: O(n) vs O(n*log(n))

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);
}

difference with ArrayList and List

Hi Here i am trying to sublist the items from list and print them 5 for each iteration.
here in the following code it is printing same items every time
for(int i=0;i<4;i++)
{
List<Card> l=a.subList(a.size()-5, a.size());
System.out.println(l);
}
But here it is printing different items as if it is removing 5 from the list each time
for(int i=0;i<4;i++){
int deckSize = a.size();
List<Card> handView = a.subList(deckSize-5, deckSize);
ArrayList<Card> hand = new ArrayList<Card>(handView);
handView.clear();
System.out.println(hand);
}
what is the difference between the above two code snippets
You should have a read at the API for List.
The returned list is backed by this
list, so non-structural changes in the
returned list are reflected in this
list, and vice-versa.
So in each case the list you are creating is not a new copy of the elements from the original list, but just a view into the original list. In the second example you are calling clear on the new list, which is actually clearing those elements in the original list, hence the behaviour you are seeing.
it seems that using .clear() on the result of .subList() removes the returned items form the original list
This is the designed behaviour of subList(). See the Javadoc for List.subList().
There's even an example doing almost exactly the same thing. (Removing items from a view)
Check out the doc for subList. It returns a list backed up by original list and is useful as a view. If you modify the sublist it will modify the main list. Since you cleared elements from it the original list is also modified.
List is interface.ArrayList is the class of implemention of List interface.

Categories