I have the following code:
System.out.println(dislist.size());
for (int k = 0; k < 10; k++) {
System.out.println(k + dislist.get(k).first + dislist.get(k).second);
if (!dislist.get(k).first.equals(Nodename)) {
if (dislist.get(k).first.equals(myfirst) ||
dislist.get(k).first.equals(mysecond) ||
dislist.get(k).second.equals(myfirst) ||
dislist.get(k).second.equals(mysecond)) {
dislist.remove(k);
}
}
}
}
The Problem is: the print at the beginning clearly says that dislist.size() is 10.
However, I get an array out of bounds exception, telling me that the size of the list is no more than 6.
And yes, I DID add new objects to the list a few lines before that.
I guess when the loop starts that has not been finished yet.
Is there a way to force Java (within the same method) to start the loop only when there is really 10 objects in the list?
You're removing elements from the List as you iterate though it. That's the reason the size is changing.
dislist.remove(k);
Create a new list, and add each element you want to remove to it. After your loop is finished, use disList.removeAll(listOfElementsToRemove) to remove them all at once.
Iterator<YourClass> iter = dislist.iterator();
while (iter.hasNext()) {
YourClass obj = iter.next();
if (/* your expression */) {
iter.remove();
}
}
Related
I was showing my code to someone and they said that it would cause undefined behavior. Being a Java programmer, that's not something I understand well. In the following code block I am iterating through scenes, which is an ArrayList, and removing elements from it.
for(int i = 0; i < scenes.size() - 1; i++)
{
if(!(Double.valueOf(scenes.get(i + 1)) - Double.valueOf(scenes.get(i)) > 10))
{
scenes.remove(i + 1);
i--;
}
}
This compiles and doesn't throw an exception at runtime, but I'm still not sure if it's a programming no-no, why it's a programming no-no, and what is the right way to do it. I've heard about using Iterator.remove() and about just creating a whole new List.
In an ArrayList, removing an element from the middle of the list requires you to shift all of the elements with a higher index down by one. This is fine if you do it once (or a small number of times), but inefficient if you do it repeatedly.
You don't really want to use an Iterator for this either, because Iterator.remove() suffers from the same issue.
A better approach to this is to go through the list, moving the elements you want to keep to their new positions; and then just remove the tail of the list at the end:
int dst = 0;
for (int src = 0; src < scenes.size(); ++dst) {
// You want to keep this element.
scenes.set(dst, scenes.get(src++));
// Now walk along the list until you find the element you want to keep.
while (src < scenes.size()
&& Double.parseDouble(scenes.get(src)) - Double.parseDouble(scenes.get(dst)) <= 10) {
// Increment the src pointer, so you won't keep the element.
++src;
}
}
// Remove the tail of the list in one go.
scenes.subList(dst, scenes.size()).clear();
(This "shift and clear" approach is what is used by ArrayList.removeIf; you can't use that directly here because you can't inspect adjacent elements in the list, you only have access to the current element).
You can take a similar approach which will also work efficiently with non-random access lists such as LinkedList. You need to avoid repeatedly calling get and set, since these are e.g. O(size) in the case of LinkedList.
In that case, you would use ListIterator instead of plain indexes:
ListIterator<String> dst = scenes.listIterator();
for (ListIterator<String> src = scenes.listIterator(); src.hasNext();) {
dst.next();
String curr = src.next();
dst.set(curr);
while (src.hasNext()
&& Double.parseDouble(src.next()) - Double.parseDouble(curr) <= 10) {}
}
scenes.subList(dst.nextIndex(), scenes.size()).clear();
Or something like this. I've not tested it, and ListIterator is always pretty confusing to use.
This is straightforward and will work for either ArrayList or LinkedList:
Iterator<String> iterator = list.iterator();
double current = 0;
double next;
boolean firstTime = true;
while (iterator.hasNext()) {
if (firstTime) {
current = Double.parseDouble(iterator.next());
firstTime = false;
} else {
next = Double.parseDouble(iterator.next());
if (next - current > 10) {
current = next;
} else {
iterator.remove();
}
}
}
I have a list of custom objects. I need to get/remove a specific object from that list but the equals implemented would not work based on what I need to search.
The following would work:
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject = list.remove(index);
// use CustomObject here
I was wondering if I could do the list.remove inside the for loop despite not using an iterator since the loop breaks immediately
Using the delete(int) method in your loop will work just fine.
Your loop is closed so you have full control on i and you can use the list as you please. You don't use i after having deleted the first element that matches, so there are no caveat. If you were to reuse it, you would have to not increment it.
To avoid any trouble, the following if both more readable and expressive. Also, it's totally implementation-agnostic.
CustomObject deletedObject = null;
for (Iterator<CustomObject> i = list.iterator(); i.hasNext(); ) {
CustomObject candidate = i.next();
if (candidate.getAttr().equals(arg)) {
deletedObject = candidate;
i.remove();
break;
}
}
if (deletedObject != null) {
// Do something with deletedObject
}
There is no special program state associated with “being inside a for loop”. What matters, are the actions your program performs.
So
int index = -1;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
index = i;
break;
}
}
CustomObject o = list.remove(index);
// use CustomObject here
is identical to
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i);
// use CustomObject here
break;
}
}
as it performs the same actions (letting aside that the first variant will throw when no match has been found). The differences regarding local variables defined in these code snippets are, well, local and do not affect anything outside the containing method.
That said, the rule that you must not modify a collection (except through the iterator) while iterating over it, applies to iterator-based loops, where you are not in control of the iterator’s internal state. When you are using an index based loop and fully understand the implications of removing an object at a particular index (of a random access list), you can even continue iterating. The important aspects, to do it correctly, are that the indices of all subsequent elements decrease by one when removing an element, further the size decreases so you must either, reread the size or decrement a previously cached size value.
E.g., the following loop is valid
for(int i = 0; i < list.size(); i++) {// rereads size on each iteration
if(list.get(i).getAttr().equals(arg)) {
CustomObject o = list.remove(i--); // decrease index after removal
// use CustomObject here
// continue
}
}
But, of course, it’s more idiomatic to use an Iterator or removeIf, as these approaches are not only easier to handle, they also work with other collections than random access lists. And especially removeIf may be more efficient when you remove more than one element.
Just another way using streams,
List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("D");
str1.add("D");
Optional<Object> foundVal = str1.stream().filter(s ->
s.contains("D")).findFirst().map(val -> {
str1.remove(val);
return val;
});
System.out.println(str1);
System.out.print(" " + foundVal.get());
Output
[A, B, D] D
I don't understand why this is happening. I was doing a bit of research on other questions and I found out that you can't modify an collection while using a for loop. However, I am using an Iterator, why is it not working?
int counter = 0;
int otherCounter = 0;
ArrayList<Character> chars = new ArrayList<Character>();
Iterator<Character> i = chars.iterator();
for (char s : e.getMessage().toCharArray()) {
chars.add(s);
}
while (i.hasNext()) {
char s = i.next();
if (chars.get(otherCounter + 1) == s) {
counter++;
} else {
counter = 0;
}
if (counter >= 2) {
i.remove();
}
otherCounter++;
}
I am getting an error on this line for some reason:
char s = i.next();
You're adding to the collection after creating the iterator.
This throws that exception.
You need to create the iterator after you finish modifying the collection.
This is because an "enhanced for loop" as you are using it creates an Iterator behind the scenes.
In fact, when you write:
for (X x: whatever) {
// do something with x
}
the "real", generated code goes something like:
Iterator<X> iterator = whatever.iterator();
while (iterator.hasNext()) {
X x = iterator.next();
// do something with x
}
And this is true if whatever implements Iterable<X> (which List does; in fact, any Collection does)
In your example, you create a List<Character>, create a first Iterator<Character> over it, and your for loop creates another Iterator<Character>, over the same list.
Note that you created the first before you modified your List... Hence the result. The first Iterator, which you reuse only afterwards, will detect that in the meanwhile, its underlying list has been modified: boom.
Very rudimentary question but I have a loop e.g.
List<ObjectList> = //set of values inside.
for(Object data : ObjectList){
// how to access next element?
// current element is accesed by 'data'. I could get the index position and then increment but is there a easier way?
}
How would you get the next element/previous? I know there are iterators i can use and so on but i want to know a neat way to do it in a for loop.
You can but don't do it as the time complexity of the loop will
increase. Just use a normal loop with an int i looping variable.
If you still want to do it you can find the index this way:
int index = lst.indexOf(data);
Then index+1 is the index of the next element.
And index-1 is the index of the previous element.
Make two methods for next and pervious and pass list and element.
public static <T> T nextElement(List<T> list,T element){
int nextIndex=list.indexOf(element)+1;
return list.size()<nextIndexlist?null:list.get(nextIndex);
}
public static <T> T previousElement(List<T> list,T element){
int previousIndex=list.indexOf(element)-1;
return list.size()>previousIndexlist?null:list.get(previousIndex);
}
1)First way
for(ObjectList data : objectList){
ObjectList previousElement=previousElement(objectList,data);
ObjectList nextElement=nextElement(objectList,data);
}
2) Second way
for(int i=0;i<=objectList.size();i++){
ObjectList previousElement=objectList.size>i-1?null:objectList.get(i-1);
ObjectList nextElement=objectList.size<i+1?null:objectList.get(i+1);
}
3) Third way using iterator
Actually, your for-each isn't iterating a List. This,
List<ObjectList> = //set of values inside.
for(Object data : ObjectList){
}
Should look something like,
List<ObjectList> al = new ArrayList<>();
for(ObjectList data : al){ // <-- like so.
}
But that won't find any data until you populate the List.
Using a "normal" for-loop, this might be, what you are looking for:
List<Object> objectList = new ArrayList<>();
// add some data
for (int i = 0; i < objectList.size(); i++) {
System.out.println((i > 0) ? "previous Object: " + objectList.get(i - 1) : "No previous object, current is the first one.");
System.out.println("Current Object: " + objectList.get(i));
System.out.println((i < objectList.size()) ? "Next Object: " + objectList.get(i + 1) : "No next object, current is the last one.");
}
Key aspect is, that you have to use your loop variable (i in this case) to access your actual elements. i + 1 gives you the next element and i - 1 the previous.
I think what you is an iterator, its used like this:
List<ObjectList> list= //set of values inside.
Iterator<ObjectList> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
System.out.println(iterator.previous());
iterator.next()
}
It allows you to access the next and previous objects.
ListIterator:
There is the ListIterator which can a bit of stepping back and forth.
Mind in the code below previousIndex() yields -1 at the start.
for (ListIterator<Object> iter = objectList.listIterator(); iter.hasNext(); ) {
Object object = iter.next();
Object previous = objectList.get(iter.previousIndex()); // Might fail
Object next = objectList.get(iter.nextIndex()); // Might fail
if (iter.hasPrevious()) ... iter.previous();
}
This simple piece of code removes any Statistic in an arrayList if its properties are null. Unfortunately, I'm getting a java.lang.NullPointerException on the first line of the if Statement (if tempStat.serialName==null...). Thoughts?
public static ArrayList<Statistic> removeEmptyStats(ArrayList<Statistic> al) {
Statistic tempStat=new Statistic();
for (int i=0; i<al.size(); i++) {
tempStat=al.get(i);
if (tempStat.serialName==null || tempStat.serialPublisher==null || tempStat.serialISSN==null || tempStat.serialOnlineISSN==null) {
al.remove(i);
}
}
return al;
}
It must be because tempStat itself is null. However you need to be very careful with this.
Say you have 5 elements in your list element 2 is invalid so you remove it, element 3 has now dropped back to element 2, but you're going to move on to check element 3 next, however this is now the old element 4 (if that makes any sense!)
You should not remove entries of a list on which you are doing a loop.
tempstat is null, because when doing for (int i=0; i<al.size(); i++), it stores the initial value of al.size().
Then when you are doing al.get(i) after having removed some values, you can have a i which is higher than the size of the list. (BTW, once you remove a value, it also mean that you will skip one in your list).
You'd to have to use an iterator and iterator.remove().
Iterator<Statistic> iterator = al.iterator();
while(iterator.hasNext()) {
Statistic tempStat = iterator.next();
if (tempStat.serialName==null || tempStat.serialPublisher==null || tempStat.serialISSN==null || tempStat.serialOnlineISSN==null) {
iterator.remove();
}
}
The variable tempStat is null! check the values contained in passed array al