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<>();.
Related
when running the code below i get the above exception but i don't know why or how to fix it.
Im pretty sure it comes from
for(int node : adjacent(currentnode))
{
//System.out.println(adjacent(currentnode));
//System.out.println(node);
if (remainingnodes.contains(getNode(node)))
{
adjacent.add(node);
remainingnodes.remove(getNode(node));
//System.out.println(remainingnodes);
}
}
getNode just takes an integer and returns the corresponding node. I used to not get the exception before i used getNode in remainingnodes.contains but at the time it was removing the components so i had to change it and now i get the exception.
public int distance(int target, List<Integer> detectives)
{
List<Integer> adjacent = new ArrayList<>();
Set<Node<Integer>> remainingnodes = new HashSet<Node<Integer>>();
List<Integer> currentnodes = new ArrayList<>();
int distance = 0;
int i = 0;
currentnodes.add(target);
remainingnodes = graph.getNodes();
remainingnodes.remove(getNode(target));
while (detectives.size() != 0)
{
for (int currentnode : currentnodes)
{
for(int node : adjacent(currentnode))
{
//System.out.println(adjacent(currentnode));
//System.out.println(node);
if (remainingnodes.contains(getNode(node)))
{
adjacent.add(node);
remainingnodes.remove(getNode(node));
//System.out.println(remainingnodes);
}
}
for (int detective : detectives)
{
if (currentnode == detective)
{
distance = distance + i;
detectives.remove(detective);
}
}
}
currentnodes.clear();
currentnodes = adjacent;
i++;
}
Thanks
Arthur
You cant modify the List in for each loop. If you want to remove any elements in loop use iterator. You can remove elements using iterator.remove(); which deletes current element in the iterator.
As you are itterating over a list and trying to remove element from the same list, a ConcurrentModificationException shall occur.
You first iterate over the list then you determine the position of the element you want to remove from list and store it in a temporary variable, then after the iteration is complete, just remove the particular element based on stored position.
Your assumption is correct. While iterating over a List, you cannot alter it, like you are doing when calling adjacent.add(node) inside the for loop.
This is called concurrent modification, thus the exception ConcurrentModificationException.
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.
I have an ArrayList of object containing a reference to their parent object.
I am trying to remove every objects within a specific parent (so equals to the parent object).
If I do:
System.out.println(parent);
my console output:
ParentObject#1f8166e5
if I do:
for(int i = 0; i < array.size(); i++){
if(array.get(i).getParent().equals(parent)){
array.remove(i);
}
}
And (test)
for(int i = 0; i < array.size(); i++){
if(array.get(i).getParent().equals(parent)){
System.out.println(parent + ":" + array.get(i).getParent());
}
}
My console output something like:
ParentObject#1f8166e5:ParentObject#1f8166e5
What's wrong with the snippet above?
Why array.remove(i) did not work?
I suspect the problem is that after you've removed element i (which moves everything after it up the list - the element at index n + 1 now has index n etc) you're then skipping the next element. So if you have two consecutive elements to remove, you're missing the second one. The simplest fix is to work from the end:
for (int i = array.size() - 1; i >= 0; i--) {
if (array.get(i).getParent().equals(parent)) {
array.remove(i);
}
}
EDIT: As noted in another answer, it's generally better to use an iterator for removal anyway. In your case, this would look like this:
// We don't know what the Foo type is...
for (Iterator<Foo> iterator = array.iterator(); iterator.hasNext(); ) {
Foo element = iterator.next();
if (element.getParent().equals(parent)) {
iterator.remove(); // Note: *not* array.remove(...)
}
}
You can't remove objects while iterating. You should use Iterator if you want to remove element while iterating
When I have a for loop, I use the i to refer to the elements of my array, objects, etc.
Like:
Current item: myArray[i]
Next item: myArray[i+1]
Previous item: myArray[i-1]
But at the moment, I'm using a foreach loop ( for (Object elem : col) { ).
How do I refer to the previous item?
(I need to do a search an 'array', which I'm doing with for (Object object : getComponents()).
But when it returns true (so it finds what I look for), it should perform the code on the previous and the next item.
Clarification: I have java.awt.Component elements!
If the data-structure is a List, then you can use a ListIterator directly. The ListIterator is special because it contains both the methods next() and previous()
List list = ...;
ListIterator iter = list.listIterator(); //--only objects of type List has this
while(iter.hasNext()){
next = iter.next();
if (iter.hasPrevious()) //--note the usage of hasPrevious() method
prev = iter.previous(); //--note the usage of previous() method
}
The foreach loop won't let you do that. My suggestion is to go back to using the good old fashioned Iterator. For example
final Iterator itr=getComponents().iterator();
Object previous=itr.next();
Object current=itr.next();
while(itr.hasNext()){
Object next=itr.next();
//Do something with previous, current, and next.
previous=current;
current=next;
}
JButton prev, next, curr;
Component[] arr = getComponents();
for(int i=1;i<arr.length-1;i++) {
if (yourcondition == true) {
curr = (JButton) arr[i];
prev = (JButton) arr[i-1];
next = (JButton) arr[i+1];
}
}
I did this to access the previous and next elements in a list while using enhanced for loop.
Here's a quick snippet:
import java.util.ArrayList;
import java.util.List;
class Scratch {
public static void main(String[] args) {
List<Integer> myInts = new ArrayList<>();
myInts.add(1);
myInts.add(2);
myInts.add(3);
myInts.add(4);
myInts.add(5);
Integer next = null;
Integer previous = null;
for (Integer current: myInts) {
try {
previous = myInts.get(myInts.indexOf(current)-1);
} catch (IndexOutOfBoundsException ignored){
// ignored
}
try {
next = myInts.get(myInts.indexOf(current)+1);
} catch (IndexOutOfBoundsException ignored){
next = null;
}
System.out.println("previous = " + previous);
System.out.println("current = " + current);
System.out.println("next = " + next);
}
}
}
Output:
previous = null
current = 1
next = 2
previous = 1
current = 2
next = 3
previous = 2
current = 3
next = 4
previous = 3
current = 4
next = 5
previous = 4
current = 5
next = null
Yeah! I know, the code's ugly. But, it get's the job done.
Array indexing
If you have an array-like data-structure (e.g. an actual array or something like an ArrayList), then referencing i, i-1, i+1 will give good performance so there isn't much more to it. (Although having to turn a For-Each Loop into an index counting For Loop isn't very fun and is one of the few caveats.)
The answer offered by Sergey does something like this.
The versatile ListIterator
If you can get your hands on an ListIterator (which is actually quite a big assumption), the answer offered by Suraj could suffice. But do note both next() and previous() moves the iterator position. So if you did something like the following for each loop iteration: prev = previous(); current = next(); next = next(); previous(), you'll end up performing roughly 4 iteration operations per loop. This isn't much of a problem if iteration is cheap, and luckily this is often the case for data-structures that offers a ListIterator.
Generic solution
The generic solution for any Iterable (or Iterator) should make no random lookups (as is possible with an array) or make assumptions regarding performance of next(), which should be called at most N times where N is the number of available elements.
Here's one such implementations:
final Iterator<E> it = iterable.iterator();
for (E next = (it.hasNext() ? it.next() : null), current = null; next != null;) {
E previous = current;
current = next;
next = it.hasNext() ? it.next() : null;
// Do something using 'current', 'previous' and 'next'.
// NB: 'previous' and/or 'next' are null when 'current' is
// the first and/or last element respectively
}
Mind, this implementation has caveats of its own:
It'll break if Iterable contains null elements.
Neither current or next are effectively-final, so cannot be used directly in them fangled Java 8 lambdas.
I'm trying to replace an iterator-based loop over a Java list with a for-each statement, but the code uses at some point iterator.hasNext() to check if it reached the last element in the list.
Is there something similar for the for-each alternative?
for (Object current : objectList) {
if (last-element)
do-something-special
}
for-each is just syntactic sugar for iterator version and if you check compiled bytecode, then you'll notice that compilator actually change it into iterator version.
With a for-each form you can't check whether you'll have more elements or not.
Just stay with explicit iterator use if you need that feature.
In addition to Luno's answer:
Iterator<MyClass> it = myCollection.iterator();
while(it.hasNext()) {
MyClass myClass = it.next():
// do something with myClass
}
translates to:
for (MyClass myClass:myCollection) {
// do something with myClass
}
As others have said - this isn't possible.
Just remember that the foreach construct isn't the be-all and end-all. It was introduced to make the very common task of performing the same operations on each element of a collection simpler to denote.
In your case, you don't want to do exactly the same thing to each element - and thus a foreach loop is not the right tool for the job. Trying to "hack" it into doing this is silly, just use an explicit iterator in a classic for loop.
The foreach loop (or enhanced for loop) does not have facilities to keep track of which element is being iterated on at the moment. There is no way to find out which index of a Collection is being worked on, or whether there are more elements to be processed in an Iterable.
That said, one workaround that would work is to keep a reference to the object which is being iterated on at the moment in the foreach loop.
By keeping a reference of what it being worked on at the current iteration, one would be able to keep the reference once the foreach loop ends, and what is left in the variable will be the last element.
This workaround will only work if-and-only-if the last element is the only element which is needed.
For example:
String lastString = null;
for (String s : new String[] {"a", "b", "c"}) {
// Do something.
// Keep the reference to the current object being iterated on.
lastString = s;
}
System.out.println(lastString);
Output:
c
Unfortunately, the for each idiom does not allow you to check if an element is first or last in the list. This is a known limitation of the for each loop.
I suggest you just keep using the iterator.
If you can also check for the first element instead of the last one, for example if you're doing String concatenation, you could change to something like:
boolean first = true;
for (Element e : list) {
if (!first) {
//do optional operation
}
//do other stuff
first = false;
}
but I would prefer using the iterator.
If you want to stay with for-each maybe something like this:
if (objectList.indexOf(current)==objectList.size()-1) break;
int nElts = objectList.size();
int n = 0;
for (...) {
if (++n == nElts) ...
is the best I can think of.
There are two possible cases where you would like to do this.
You need to do something after the last element has been reached: in this case you just need to put your code outside of the loop.
for(Object item:theLinkedList){
}
System.out.println("something special")
you need to modify the last element in some way or use information related to the last element. In this case you should use the **LinkedList** to access the last element
for(Object item:theLinkedList){
}
Object last=theLinkedList.getLast();
System.out.println("last");
yes you can, here's how i would do it if you don't want to use the explicit iterator syntax:
for (Object current : objectList) {
if (objectList.getLast().equals(current))
do-something-special
}
In Addition to bayer you have to do it a bit different because there is no method getLast(). But instead of it you can use this objectList.size() - 1.
for (Object current : objectList) {
if (objectList.get(objectList.size() - 1).equals(current))
do-something-special
}
Just save loop repeat count, sample :
int loop = 0;
for(Item item : items) {
if(loop == 0) {
//Is First Item
}
if(loop != items.size()-1) {
//Has Next Item
}
if(loop == items.size()-1) {
//Is Last Item
}
//Must Be Last Statement
loop++;
}
It's similar to for(int i = 0; i < items.size(); i++) loop;
items.size() is used for lists and items.length for arrays;