Could we remove while iterating if we just remove 1 element? - java

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

Related

Iterate over one collection and get the same index value of another collection

I want know is it even possible in Java to iterate over, let say a list, and get/set the same index(int) value of other list?
for (Response e : responseList) {
e.setPrimarySkills(requestList.get(??).getPrimarySkills());
}
Since it can't be done through model mapper because of the issues, is there any neat way of doing the same ?
Using two iterators:
Iterator<Response> responseIt = responseList.iterator();
Iterator<Request> requestIt = requestList.iterator();
while(responseIt.hasNext() && requestIt.hasNext()) {
Response response = responseIt.next();
Request request = requestIt.next();
...
}
[Recommended for its clarity]
Using Guava Streams.forEachPair :
Streams.forEachPair(
requestList.stream(),
responseList.stream(),
(req, resp) -> resp.setPrimarySkills(req.getPrimarySkills())
);
Either, don't do it with a for-each loop, use an indexed for loop.
for (int i = 0; i < responseList.size(); ++i) {
responseList.get(i).setPrimarySkills(requestList.get(i).getPrimarySkills());
}
Or, use a pair of Iterators:
Iterator<Response> responseIt = responseList.iterator();
Iterator<Request> requestIt = requestList.iterator();
while (responseIt.hasNext() && requestIt.hasNext()) {
// Put responseIt.next() and requestIt.next() into variables, if you want.
responseIt.next().setPrimarySkills(requestIt.next().getPrimarySkills());
}
The advantage of Iterators over an index is that Iterators are efficient for non-RandomAccess lists; but, unless your lists are big, it's unlikely to be a significant (or even noticable) difference.
You can do it with a for-each loop, by maintaining the index yourself, but it's a bit inconsistent in the treatment of the two lists:
int i = 0;
for (Response e : responseList) {
e.setPrimarySkills(requestList.get(i).getPrimarySkills());
i++;
}
You can use a for-loop with incrementing index:
List<String> l1 = List.of("a", "b");
List<Integer> l2 = List.of(1, 2);
for(int i=0; i<l1.size(); i++) {
String s = l1.get(i);
Integer i = l2.get(i);
}
Of course you should first make sure that both lists have equal length, to avoid OutOfBounds Exception.

Is removing elements from an ArrayList or LinkedList while iterating through it with a for loop in Java bad? If so, why?

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

How would you make a function with a while and for loop to return the last item in a list?

Say I had a method finalNum() where I want to use this method to pick out the final number in a list i.e. {1,2,10,12} and returns 12
What is the simplest way to do so using a for loop vs a while loop? Can someone show both? I'm new to code and trying to find a simple example.
Since Lists are based on arrays which are zero-indexed, the last element is the element at index list.size() - 1. Using List#get will suffice; there is no need for looping at all.
final List<Integer> list = List.of(1,2,10,12);
final Integer last = list.get(list.size() - 1);//12
If you really want to solve this question for educating purposes, you can take a look at these examples.
Here we loop from the beginning to the end and saving each element of the list to a variable. After we have finished looping we return the value which is by definition the last element of the list
static int finalNum(List<Integer> listOfNumbers) {
int result = 0;
for (int i = 0 ; i < listOfNumbers.length(); ++i) {
result = listOfNumbers.get(i);
}
return result;
}
You can do it the other way around, too:
static int finalNum(List<Integer> listOfNumbers) {
int result = 0;
for (int i = listOfNumbers.length() - 1; i >= 0; --i) {
return listOfNumbers.get(i);
}
throw new IllegalStateException("list contains no elements");
}
That loop goes from the end to the beginning. So the loop does not actually loop. This is mostly not what the developer wants or has intended.
The first example as a while loop
static int finalNum(List<Integer> listOfNumbers) {
int result = 0;
Iterator<Integer> iterator = listOfNumbers.iterator();
while(iterator.hasNext())
result = iterator.next();
}
return result;
}
Here we also loop from the beginning to the end storing everything in a variable which we return as result at the very end.
But after all, none of this is recommend as you can easily access the last element of a list via list.get(list.size() - 1). The above code is by no means code for production.

Using LinkedList to access a get method

Direct is a class that contains 2 get methods and one of them is getName().
In the following code, I am using an array and it works correctly.
But if I want to store it in a LinkedList instead of an array, how do I iterate and reach the getName() method. I am able to iterate fine if is just a list of common primitives such as Strings but in this case where it is a list of class, I am confused on how to reach the getName() method. Thanks for helping.
private LinkedList<Direct> directList= new LinkedList();
private ListIterator<Direct> iterator = directList.listIterator();
private Direct[] direct = new Direct[100];
private int find(String name){
for (int x=0; x < direct.length; x++){
if (direct[x] != null)
if (direct[x].getName().equals(name)){
return x;
}
}
return -1;
}
Simply use directList.get(i). But you shouldn't use the index based get() method with LinkedList as it's very slow. Instead, you should use an iterator (or a for each loop, which is essentially the same):
int cnt = 0;
List<Direct> list = new LinkedList<>();
for (Direct d : list) {
if (name.equals(d.getName())) {
return cnt;
}
cnt++;
}
With an iterator:
for (Iterator<Direct> it = list.iterator(); it.hasNext();) {
Direct d = it.next();
if(name.equals(d.getName())){
System.out.println("matches");
}
}
In Java 8 you can also use the following solution (which will be slower, as it filters the entire list):
Direct d = list.stream().filter(direct -> direct.getName().equals(name)).findFirst();
There are (at least) two ways:
// generally easier to read if you don't need access to the iteration number.
for( Direct d : directList )
{
d.getName();
// ...
}
or use List#get(int) method (Although this is valid, since you are using a LinkedList this solution is O(n^2) instead of O(n) it shouldn't be used.)
for( int i = 0; i < directList.size(); ++i )
{
directList.get(i).getName();
}

Java concurrent modification

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

Categories