Remove an array of ids contained in another array - java

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

Related

Replacing old list with new list java

so basically right now I have a list, in order to remove invalid items from the list I'm looping through it and saving all valid items to a new list. After the loop is done I want to set the old list equal to the new list. I'm just wondering, if I go
list = newList;
will list now be a pointer to newList? Or will I have 2 completely independent lists now.
Thanks
You will be assigning to list a reference to the same list that newList references. After the assignment, there will be only one list involved (list and newList will reference the same object). Also, unless you have another reference to the object that list used to reference, the object that list used to reference will then be garbage collected.
Following up on the comment by SamV, if the list supports deletion, you don't need two lists for this operation:
List<Thing> list = . . .;
for (Iterator<Thing> iter = list.iterator(); iter.hasNext(); ) {
Thing t = iter.next();
if (!isValid(t)) {
iter.remove(); // removal through the iterator itself is ok
}
}
You can simply iterate over that list, then check for validity, if not a valid just remove it.
for (Iterator<SomeThing> itr = oldList.iterator(); itr.hasNext();) {
SomeThing smt = itr.next();
if(!valid) {
itr.remove();
}
}
In this way you don't have to create a new list, then fill it and then assign it to the old one, you will skip all that code.
When you use this list = newList; the value of newList, which is the reference to your list with valid items is copied to the list.
So yeah, now list and newList will be referencing to the exactly same object.
You might consider whether mutating the list is necessary. Removing items from a list that have two variables pointed to it will only add confusion. Variable are labels and if they are getting assigned in multiple places their meaning changes. It sounds like you would like to filter the list based on a predicate. With Java 8 you can do the following.
List<String> words = new ArrayList<String>(Arrays.asList("foo", "bar", "baz"));
List<String> wordsStartingWithB = // filtered list
words.stream()
.filter(word -> word.toUpperCase().startsWith("B"))
.collect(Collectors.toList());
wordsStartingWithB.stream()
.forEach(s -> System.out.println(s));
// bar
// baz
This allow words to remain intact in the original form while the wordsStartingWithB will contain only the items you want. It also give you the advantage of being able to give the variable a meaningful name since its meaning will not change later on. Try putting final in front of your variables to see how it causes you to rethink the problem.
The java.util.Stream adds many other useful methods. You can replace .stream() with parallelStream() when it makes sense to take advantage of running in parallel.
http://download.java.net/jdk8/docs/api/java/util/stream/Stream.html

Guava Iterables.filter(), how do you test for an empty return?

I love using Guava's Iterables filters. But one common code snippet looks like:
List foo = Lists.newArrayList(Iterables.filter(someCollection, somePredicate));
if (foo.isEmpty) {
// do empty
} else {
int count = foo.size(); // do non-empty
}
This is wasteful, as I don't really need to build the "foo" list ever, all I need is to know if its empty or not and get a count of the number of elements.
I'd like to know the best practices for:
1) how do I test for isEmpty() without wasting time building the list into memory
2) Is there a way to get the size without iterating through all of the entries?
3) if there is no non-iterating solution to #2, is it better to just iterate and do count++?
FluentIterable.from(foo).filter(predicate).isEmpty() doesn't iterate any more than it has to.
But if you need the size, then you really want to use FluentIterable.from(foo).filter(predicate).size(), which won't store the elements but will just count up the ones that match the predicate.
Louis's answer is on the mark. Since you happen to be using a Collection, could also use Collections2.filter:
Collection<Foo> filteredFoos = Collections2.filter(foos, fooPredicate);
if (filteredFoos.isEmpty()) {
...
}
From the documentation:
Returns the elements of unfiltered that satisfy a predicate. The
returned collection is a live view of unfiltered; changes to one
affect the other.
There's no way to get the size of a filtered Iterable without iterating because you don't know which elements will match. Why not just use Iterables.size? If no elements match your predicate, isEmpty will have to iterate through the whole collection anyway.

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