Java how to remove element from List efficiently - java

Ok, this is a proof-of-concept I have on my head that has been bugging me for a few days:
Let's say I have:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
if (!str.equals("foo") || !str.equals("bar")) a.remove(str);
}
this would end up with the list ["foo", "bazz", "bar"] because it would read the string at index 1 ("buzz"), delete it, the string at index 2 ("bazz") would jump to index 1 and it would be bypassed without being verified.
What I came up with was:
List<String> a = new ArrayList<String>();
a.add("foo");
a.add("buzz");
a.add("bazz");
a.add("bar");
for (int i = 0; i < a.size(); i++)
{
String str = a.get(i);
boolean removed = false;
if (!str.equals("foo") || !str.equals("bar"))
{
a.remove(str);
removed = true;
}
if (removed) i--;
}
It should work this way (atleast it does in my head lol), but messing with for iterators is not really good practice.
Other way I thought would be creating a "removal list" and add items to that list that needed to be removed from list a, but that would be just plain resource waste.
So, what is the best practice to remove items from a list efficiently?

Use an Iterator instead and use Iterator#remove method:
for (Iterator<String> it = a.iterator(); it.hasNext(); ) {
String str = it.next();
if (!str.equals("foo") || !str.equals("bar")) {
it.remove();
}
}
From your question:
messing with for iterators is not really good practice
In fact, if you code oriented to interfaces and use List instead of ArrayList directly, using get method could become into navigating through all the collection to get the desired element (for example, if you have a List backed by a single linked list). So, the best practice here would be using iterators instead of using get.
what is the best practice to remove items from a list efficiently?
Not only for Lists, but for any Collection that supports Iterable, and assuming you don't have an index or some sort of key (like in a Map) to directly access to an element, the best way to remove an element would be using Iterator#remove.

You have three main choices:
Use an Iterator, since it has that handy remove method on it. :-)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (/*...you want to remove `it.next()`...*/) {
it.remove();
}
}
Loop backward through the list, so that if you remove something, it doesn't matter for the next iteration. This also has the advantage of only calling list.size() once.
for (int index = list.size() - 1; index >= 0; --index) {
// ...check and optionally remove here...
}
Use a while loop instead, and only increment the index variable if you don't remove the item.
int index = 0;
while (index < list.size()) {
if (/*...you want to remove the item...*/) {
list.removeAt(index);
} else {
// Not removing, move to the next
++index;
}
}
Remember that unless you know you're dealing with an ArrayList, the cost of List#get(int) may be high (it may be a traversal). But if you know you're dealing with ArrayList (or similar), then...

Your first example will likely cause off-by-one errors, since once you remove an object your list's indexes will change. If you want to be quick about it, use an iterator or List's own .remove() function:
Iterator<String> itr = yourList.iterator();
while (itr.hasNext()) {
if ("foo".equals(itr.next()) {
itr.remove();
}
}
Or:
yourList.remove("foo");
yourList.removeAll("foo"); // removes all

ArrayList.retainAll has a "smart" implementation that does the right thing to be linear time. You can just use list.retainAll(Arrays.asList("foo", "bar")) and you'll get the fast implementation in that one line.

Related

Does the 'hasNext' must be called before the 'next' if the size is known in java?

If the size of list is known, only call the 'next' without 'hasNext'. Is it right?
final List<Integer> list = [1, 2, 3];
final Iterator<Integer> iter = list.iterator();
for(int i = 0; i < list.size(); ++i){
System.out.println(iter.next());
}
If there is no next element and you still call next(), you'll get NoSuchElementException. To protect against this, you need to do a pre-check using hasNext().
In your example, you already know the size and the condition i < list.size() is guarding you against trying to jump after the last element, so there is no point calling hasNext().
We generally do:
while (iterator.hasNext()) { //protection against jumping after the last element
//call next()
}
You have done a similar thing, just the "protection" is a bit different (but valid):
for(... i < list.size() ...) { //"i < list.size()" is providing that protection
//call next()
}
So no need to use hasNext() here.
Yes, that's allowed. In your example, there isn't much point to it (you'd be better off either using list.get(i) or using hasNext()/next()), but it doesn't break anything.
One place where you do see next() without hasNext() is to get an arbitrary item out of a collection that is known not to be empty. This idiom comes up sometimes:
Collection<T> myCollection = ...;
if (!myCollection.isEmpty()) {
return myCollection.iterator().next();
}

Finding an element with the Java For each loop

I want to loop through a LinkedList using For each and access the element after the one I am at right now. If I would use an Array and the usual for(int i = 0; i < ... ; i++), I would be looking for the element i+1. Is there a possibility of reaching the element after the one I am at right now?
Is there a possibility of reaching the element after the one I am at right now?
If you use a simple "for each" loop, then No. The "current position" within the list is encoded to the list's Iterator. When you use "for each", the Iterator is hidden from your application.
Instead you need to do something like this:
List<Element> list = ...
Element current = null;
Element next = null;
Iterator<Element> it = list.iterator();
while (it.hasNext()) {
current = next;
next = it.next();
if (current == null) {
continue;
}
// process 'current' and use `next` when you need to refer to
// the following on element.
}
if (next != null) {
// process 'next' as the last element.
}
Notes:
If null is a valid list element, then use a flag instead.
Using two iterators for the same list might give you a neater solution.
You could also do something similar to this using a for each ... and keeping track of the "previous" element in a variable, like I do above. See #Jezor's answer.
It is inadvisable to use get(i) on a (large) LinkedList because get(int) is an O(N) operation for a LinkedList in the general case.
You can neither determine the index of next element in enhanced for loop nor it's value.
You can keep the reference to previous element though:
// List is initialized somewhere in the code.
List<Element> elements = ...;
Element previousElement = null;
for (Element element: elements) {
if (previousElement != null) {
// ... your code here...
// previousElement is elements[i]
// element is elements[i + 1]
}
previousElement = element;
}
In the above example you can instantiate previousElement with a null object to avoid the null check.
What you really want to use is good, old for loop with a counter:
List<Element> elements = ...;
for (int i = 0; i < elements.size(); ++i) {
// elements.get(i) is elements[i]
// elements.get(i + 1) is elements[i + 1]
// But be careful, (i + 1) will be out of bounds for the last element!
}
Don't make your life unnecessarily complicated.
If you're using the auto-iterator for-each style loop such as:
for (T elem: myList) {
// ...
}
then there isn't an immediate way to access the "next" element while you're looking at the current one without saving a reference to the element you last saw. It is also possible with using indexOf, but this approach gets very messy when duplicates are found in the list. Regardless, I think your problem would be better addressed using indexing over the list instead.
If you're iterating over your LinkedList object using indexed-based access (as opposed to an iterator or for-each, for example,) you can do:
for (int i = 0; i < myList.size(); i++) {
// ...
if ((i + 1) < myList.size()) {
T nextElem = myList.get(i+1); // fetch the element at the next index
// then do whatever you want with "nextElem"
}
// ...
}
where T refers to the type of LinkedList you are using, i.e. String if your list definition was something like LinkedList<String> myList = new LinkedList<>();.

How to compare couples of items within an ArrayList using for-each loop?

I have a vector of object and I need to compare them 1-by-1. That is:
for (Object o1 : list) {
for (Object o2 : list) {
//Do something with o1 and o2
}
}
Anyway using this approach I will compare any couple twice!
Using a "C" style approach, I would have done this:
for (i=0; i<n-1; i++) {
for (j=i+1; j<n; j++) {
//Do something with list[i] and list[j]
}
}
where n is the length of the list.
Is there a way to do this using for-each loop?
Addition
The usage of a for-each loop is not mandatory. Anyway, I'm concerned with performances issues. Does the for-each loop is faster than a simple for accessing index directly or not?
It is explicitly clear what your intent is with the C-like for loops - loop over every pair exactly once, so that the same pair with reversed members, e.g. (a, b) and (b, a) doesn't get processed again. That is what I would recommend; it works in Java just as well.
However, if you absolutely have to have the enhanced for loop, you can have the inner for loop operate over a sublist, with List's subList method, starting with the next element.
for (Object o1 : list) {
List<Object> subList = list.subList(list.indexOf(o1) + 1, list.size());
for (Object o2 : subList) {
//Do something with o1 and o2
}
}
The enhanced for loop is not appropriate in all situations. If you avoid using an index, just to use indexOf in the loop, your code will be less efficient (indexOf is a linear search) and non-idiomatic.
In my opinion, the best answer is to use the explicit indices.
for (i=0; i<n-1; i++) {
for (j=i+1; j<n; j++) {
// do something with list.get(i) and list.get(j)
}
}
One situation where you should avoid using get is if the List is a LinkedList, because get for a LinkedList is not a constant time operation. In this case I would do
List<Object> copy = new ArrayList<>(linkedList);
for (i=0; i<n-1; i++) {
for (j=i+1; j<n; j++) {
// do something with copy.get(i) and copy.get(j)
}
}
Assuming list is declared as List<Whatever>, you may achieve this behavior properly by using ListIterator rather than for-each loops:
ListIterator<Whatever> iteratorI = list.listIterator();
if (iteratorI.hasNext()) {
for (Whatever whateverI = iteratorI.next(); iteratorI.hasNext(); whateverI = iteratorI.next()) {
ListIterator<Whatever> iteratorJ = list.listIterator(iteratorI.nextIndex());
for (Whatever whateverJ = iteratorJ.next(); iteratorj.hasNext(); whateverJ = iteratorJ.next()) {
//do the comparison here...
}
}
}
The comments suggest you do it c-style or track an explicit index. Those are good suggestions. But if you insist on doing it with the new style for loop, there is a way:
for (Object o1 : list ) {
final int o1Index = list.indexOf(o1);
final int listSize = list.size();
for (Object o2 : list.subList(o1Index + 1, listSize)) {
//Do something with o1 and o2
}
}
The idea is that any o2 that comes before o1 in the list has already been processed, and you don't need to process o1 against itself. So you get a sublist consisting only of the elements following o1 and draw o2 from that sublist.
That will fail if any elements appear more than once in the list.
I've broken out o1Index and listSize for clarity, but in practice you'd probably in-line them.
Another option is to copy the original list and, before starting the inner loop, remove the front element each time. This properly accounts for duplicate elements, but takes more space.
final List<Object> newList = new ArrayList<>(list);
for (Object o1 : list) {
newList.remove(0);
for (Object o2 : newList) {
// Do something
}
}
You want performance?
Here you go!
The usage of a for-each loop is not mandatory
int s = list.size();
for(int i = 0; i < s-1; i++){
for(int n = i+1; n< s;n++){
if(list.get(i).equals(list.get(n))){
System.out.println("Duplicate");
}
}
}
You will never compare a combination twice.
Also to fully answer you question:
foreach does require more resources and reduces performance
To achieve the same result with foreach statement you'd create very much heap and slow the application down, also more instructions are processed by the CPU so you don't just loose memory but computing performance too.
Also try avoiding to call the size() method more than one time hence your list is not modified within this procedure. This also reduces CPU usage, but requires a very little bit more RAM (int s).
So your "C" style approach is almost optimal.
For ease i used java api calls, it also should be a ease to usw this example on your target framework.
EDIT: Improve performance even more by saving the list's size to reduce method calls.
This is a very specific case, comparison is just an operation of an infinity of other operations, other non commutative operations have a meaning for all combinations (this is just an example).
So there is no enhanced loop for such case.

Iterate through a list deleting elements

I want to iterate through some kind of list, checking if its elements meet a property, and if they don't deleting them from the array. What I've thought is something like this:
int index = 0;
for(int i = 0; i < list.size(); ++i) {
if(list.isProperty()) list.delete(index) //We delete the element at list[index]
else ++index;
}
Maybe those aren't the real methods of the list interface in java, but they're quite self-explanatory.
Is this a good approach? Which data structure would fit best if I have to run this operation many times? I don't think an arrayList would work as I'd have to be moving around elements each time I delete and I can't ensure the elements I'll remove are in the head or the tail of the list either.
You can achieve it using iterator.Without having concurrent modification exception.
Say your list consists of object A
List<A> list = new ArrayList<A>();
Iterator<A> iterator = list.iterator();
while (iterator.hasNext()) {
A current = iterator.next();
if(current.isProperty()) {
iterator.remove();;
}
}
You should remove an element from a List using an Iterator. You can use this with ArrayList.
List<YourDataType> yourList = new ArrayList<YourDataType>();
Iterator<YourDataType> it = yourList.iterator();
while (it.hasNext())
it.remove();
With this you can use if-else to specify the element, which should be removed.
This should give you some hints, why you should use an Iterator.

How to list elements maintaining order and reverse iteration?

What is the best list/set/array in Java that combines the following aspects:
maintain order of added elements
make if possible to both iterate forwards and backwards
of course good performance
I thought about a LinkedList, I then could insert elements by add(0, element) which would simulate a reverse order. Most of the time I will be using backwards iteration, so using this I can just iterate trough.
And if not, I can list.listIterator().hasPrevious().
But are there better approaches?
ArrayList will probably be your best bet. You can iterate through it in the following manner:
for (ListIterator it = list.listIterator(list.size()); it.hasPrevious();) {
Object value = it.previous();
}
A LinkedList will work but it will have more object creation overhead since you need to instantiate a Link for each element you store.
If you can get by index and wish to iterate over the collection then you can use a List and get(index) allow you to get the object in that place in the list. Arrays allow you to do this, you can just reference the index as normal, however if your array might grow then a Collection is going to be easier to use.
You can use List.size() and element through the object using a for loop rather than using an Iterator object, this will allow you to iterator over the list both forwards and backwards. For example:
List<AnObject> myList = new ArrayList<AnObject>;
// Add things to the list
for (int i = 0 ; i < myList.size; i++) {
AnObject myObject = myList.get(i);
}
for (int i = myList.size()-1 ; i <= 0 ; i--) {
AnObject myObject = myList.get(i);
}
Set is not applicable as a Set does not maintain ordering.

Categories

Resources