Reputation: 4405
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
Upvotes: 1
Views: 138
Reputation: 298153
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.
Upvotes: 2
Reputation: 647
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
Upvotes: 0
Reputation: 35427
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
}
Upvotes: 2