ArrayList.remove is not working in a loop - java

I have following code-
import java.util.ArrayList;
public class ArrayListExp{
public static void main (String[] args){
ArrayList<String> name = new ArrayList<String>();
name.add("Chris");
name.add("Lois");
name.add("Meg");
name.add("Meg");
name.add("Brain");
name.add("Peter");
name.add("Stewie");
System.out.println(name);
for ( int i = 0; i < name.size(); i++){
String oldName = name.get(i);
if(oldName.equals("Meg"))
{
name.remove(i);
}
}
System.out.println(name);
}
}
But here it gives me output -
[Chris, Lois, Meg, Meg, Brain, Peter, Stewie]
[Chris, Lois, Meg, Brain, Peter, Stewie]
I am not getting the point, why this is not removing Meg but I have tried with only one Meg in that case it is working. And I when I am adding few more Meg in last the one Meg is not removed from the ArrayList. Why?

When you remove the first "Meg", the index i=2. Then it's incremented, but since one of the "Meg" is already removed, now name.get(3) is "Brain". So you didn't actually check the second "Meg".
To fix the problem. you can decrement the index when you remove an element:
public class ArrayListExp{
public static void main (String[] args){
ArrayList<String> name = new ArrayList<String>();
name.add("Chris");
name.add("Lois");
name.add("Meg");
name.add("Meg");
name.add("Brain");
name.add("Peter");
name.add("Stewie");
System.out.println(name);
for ( int i = 0; i < name.size(); i++){
String oldName = name.get(i);
if(oldName.equals("Meg"))
{
name.remove(i);
i--;
}
}
System.out.println(name);
}
}

You are iterating over the first Meg, and when that Meg gets removed, the array values shift over by one.
[Chris, Lois, Meg, Meg, Brain, Peter, Stewie]
0 1 2 3 4 5 6
First Meg gets removed, and the loop increments i because it finished executing everything inside the for loop, so i will now be 3 and the array has been modified:
[Chris, Lois, Meg, Brain, Peter, Stewie]
0 1 2 3 4 5
Try iterating backwards.
for ( int i = name.size() - 1; i >= 0; i--){
String oldName = name.get(i);
if(oldName.equals("Meg"))
{
name.remove(i);
}
}

You can use name.removeAll(Arrays.asList("Meg")); to remove all "Meg"
Your complete code would be
for ( int i = 0; i < name.size(); i++){
String oldName = name.get(i);
if(oldName.equals("Meg"))
{
name.removeAll(Arrays.asList("Meg"));
}
}

You're removing from the ArrayList while iterating over it from 0 to N, so when you remove the first Meg at index N, the next Meg moves down to index N, then you increment i to N+1. So the 2nd Meg doesn't get removed. Try iterating in the opposite order (N to 0):
for ( int i = name.size() - 1; i >= 0; i--) {

Its because when i=2 and if condition is true then meg is deleted and all the indices are shifted up. hence the next i will point to Brain, not meg.
try this. (decrease i by one when if condition holds true)
for ( int i = 0; i < name.size(); i++){
String oldName = name.get(i);
if(oldName.equals("Meg"))
{
name.remove(i);
i--;
}
}

While removing elements you should not use for loop. It always make problems when implementing some logic. Use reverse for loop for your problem and always try to use for each.

Java 8+
The Collection interface now provides a removeIf method that modifies it in place, to which you provide a predicate that returns true if the element should be removed.
You can thus use a lambda like so:
name.removeIf(name -> name.equals("Meg"));
A method reference can be used as well to be more concise. The following code will also work if there are null elements.
name.removeIf("Meg"::equals);
If you do not want to modify the old list, you can use Stream and filter to get a List of all the items that should be retained by negating the condition.
final List<String> filtered = name.stream()
.filter(name -> !"Meg".equals(name))
.collect(Collectors.toList());
If you specifically need an ArrayList, use Collectors.toCollection with a constructor reference instead.
final ArrayList<String> filtered = name.stream()
.filter(name -> !"Meg".equals(name))
.collect(Collectors.toCollection(ArrayList::new));
Pre Java 8
The issue is that the ArrayList is being modified while being iterated over, which changes its size and shifts later elements forward; this is a problem if there are consecutive elements that need to be removed. You need to decrease the index by one each time you remove an element since that index will now refer to the next element.
for (int i = 0; i < name.size(); i++) {
String oldName = name.get(i);
if (oldName.equals("Meg")) {
name.remove(i);
i--;//Important!
}
}
Looping backwards will also fix this problem, as elements will never be shifted to a position that have yet to be checked.
for (int i = name.size() - 1; i >= 0; i--) {
String oldName = name.get(i);
if (oldName.equals("Meg")) {
name.remove(i);
}
}
Generally, using an Iterator is the most appropriate for this sort of operation, as it supports removing elements while iterating through calling Iterator#remove. This also modifies the List in place. For more complex operations, consider using a ListIterator.
final Iterator<String> it = name.iterator();
while(it.hasNext()){
final String name = it.next();
if(name.equals("Meg")){
it.remove();
}
}

Related

Why is my if loop is getting skipped while my condition is true?

I am coding a small program where I add an an ArrayList of Strings and I have a method that removes every String ending with S, it is working fine for 4 of elements but it seems to skip one.
Minimum reproducible example:
import java.util.ArrayList;
public class sList {
public static void main(String [] args) {
ArrayList<String> sList=new ArrayList<String>();
sList.add("leaf");
sList.add("leaves");
sList.add("box");
sList.add("boxes");
sList.add("phones");
sList.add("phone");
method m=new methods();
System.out.println(m.removePlurals(sList));
}
}
\\ that is my main method
import java.util.ArrayList;
public class method {
ArrayList<String> removePlurals(ArrayList<String> s) {
for (int i = 0; i < s.size(); i++) {
char c = s.get(i).charAt(s.get(i).length() - 1);
if (c == 's') {
s.remove(i);
}
}
return s;
}
}
I am getting as an output: [leaf, box, phones, phone] so it is skipping "phones"
Any help?
Think about what happens when after you removed "boxes" from the list. The index of "phones" decreases by 1. If it is originally the nth item in the list, it is now the (n-1)th item in the list, isn't it? So now "phones" is at the same position in the list as where "boxes" originally was. On the other hand, i increases by 1.
Now you should see the problem, to check for "phones", i should remain the same because after removing "boxes", the index of "phones" decreased by 1.
Solution, just loop from the end of the list to the start, and you won't have this problem:
for (int i = s.size() - 1; i >= 0; i--) {
char c = s.get(i).charAt(s.get(i).length() - 1);
if (c == 's') {
s.remove(i);
}
}
It is because at the moment you remove an item of a list, the list size is less than at the begining and when your counter is incremented, points to the next one element (skips one).
Yous can solve that just looping the list on the other way, as follow:
ArrayList<String> removePlurals(ArrayList<String> s) {
for (int i = s.size()-1; i >= 0; i--) {
char c = s.get(i).charAt(s.get(i).length() - 1);
if (c == 's') {
s.remove(i);
}
}
return s;
}
}
This is happening because the list you are iterating is being updated in the same loop. So when you remove one element is previous iteration it's index is being updated and element is not picked up at all by loop. So the solution should be like:
1) Create new list and return that:
ArrayList<String> removePlurals(final ArrayList<String> s) {
final ArrayList<String> updatedList = new ArrayList<String>();
for (int i = 0; i < s.size(); i++) {
final char c = s.get(i).charAt(s.get(i).length() - 1);
if (c != 's') {
updatedList.add(s.get(i));
}
}
return updatedList;
}
2) Using Iterator:
ArrayList<String> removePlurals(final ArrayList<String> s) {
final Iterator<String> itr = s.iterator();
while (itr.hasNext()) {
final String x = itr.next();
if (x.charAt(x.length() - 1) == 's') {
itr.remove();
}
}
return s;
}
Aside from the issue reported in the question: you have the issue that removing from the middle of an ArrayList one element at a time is really inefficient, because you keep on shifting all of the elements between (i+1) and the end of the list along by one position - and then you do it again for most of them.
Performance should not be your first concern - slow, correct code is always better than fast, incorrect code - but you should keep in the back of your mind issues that some things are worth avoiding: in this case, it is repeated removal from the middle of an ArrayList.
A better solution is not to keep shifting the elements, but rather just to move them once.
Unlike the solutions which iterate the list backwards, this can be done iterating forwards, which feels more natural (to me, at least).
int target = 0;
for (int i = 0; i < s.size(); ++i) {
if (!s.get(i).endsWith("s")) {
s.set(target, s.get(i));
target++;
}
}
This shifts each element that you are going to keep to its new position. All that then remains is to chop off the end of the list:
while (s.size() > target) s.remove(s.size()-1);
Or, in a single operation:
s.subList(target, s.size()).clear();
All together:
int target = 0;
for (int i = 0; i < s.size(); ++i) {
if (!s.get(i).endsWith("s")) {
s.set(target, s.get(i));
target++;
}
}
s.subList(target, s.size()).clear();
Note that this is effectively what is done by ArrayList.removeIf:
s.removeIf(e -> e.endsWith("s"));
(removeIf does a little bit more, in that it does the removal in a failure-atomic way, that is, if the e.endsWith fails for some reason, such as a null element, the list is left untouched rather than partly updated).

How to sort number when entering vector in java

I want to add numbers in sorted way before entering vector. But the result is not right and I am confused where the problem is ? Output is shown below.
I want to sort using some algorithm without any inbuilt methods.
import java.util.Vector;
public class Test {
public static void main(String ar[]){
//Numbers to enter in vector
int[] number = {5,2,98,3,10,1};
Vector<Integer> v = new Vector<Integer>();
v.add(number[0]);
for(int i=1;i<number.length;i++){
for(int j=v.size();j>0;j--){
System.out.println("Entered: "+number[i]);
if(number[i] <= v.get(j-1)){
v.add(j-1,number[i]);
break;
}else{
v.add(j,number[i]);
break;
}
}
}
for(int s:v)
System.out.print(s + " ");
}
}
OUTPUT:
Entered: 2
Entered: 98
Entered: 3
Entered: 10
Entered: 1
2 5 3 10 1 98
You have a second (inner) for loop based on the variable j, but that "loop" will only execute exactly one time. Both conditions inside the j loop cause the loop to exit (break;).
When you're adding each number, the only possibilities are last or next to last.
Your inner for loop doesn't actually loop.
Regardless of the condition number[i] <= v.get(j-1),
the loop will exit after one step.
What you want to do is,
iterate from the beginning of the vector,
and when you find an element that's bigger than the one you want to insert,
then insert it, and break out of the loop.
This is opposite of what you did so far, which is iterating from the end of the vector.
If the end of the loop is reached without inserting anything,
then append the value.
The program badly needs some other improvements too:
If you don't need the vector to be thread-safe, then you don't need Vector. Use ArrayList instead.
The special treatment for the first number is unnecessary.
The outer loop can be written in a more natural way using the for-each idiom.
No need to loop to print the elements, the toString implementation of Vector is already easy to read.
The variable names are very poor and can be easily improved.
The indentation is inconsistent, making the code very hard to read.
With the problem fixed and the suggestions applied:
List<Integer> list = new ArrayList<>();
for (int current : numbers) {
boolean inserted = false;
for (int j = 0; j < list.size(); j++) {
if (current <= list.get(j)) {
list.add(j, current);
inserted = true;
break;
}
}
if (!inserted) {
list.add(current);
}
}
System.out.println(list);
Last but not least, instead of searching for the insertion point by iterating over the list,
you could achieve much better performance using binary search,
especially for larger sets of values.
Another simple solution would be:
import java.util.Vector;
public class Test {
public static void main(String ar[]){
//Numbers to enter in vector
int[] number = {5,2,98,3,10,1};
Vector<Integer> v = new Vector<Integer>();
v.add(number[0]);
for(int i=1, j;i<number.length;i++){ //j declared here for better scope
for(j=v.size();j>0 && v.get(j-1)>number[i] ;j--); //<-- some changes here,
v.add(j,number[i]); //<-- and here
}
}
for(int s:v)
System.out.print(s + " ");
}
}
The inner for loop is simply used to find the right index for an element to be inserted.
Your inner loop seems to not looping more than one time. That's why the key is not being inserted into right place.
A more concise solution would be
public class Test {
public static void main(String ar[]){
//Numbers to enter in vector
int[] number = {5,2,98,3,10,1};
Vector<Integer> v = new Vector<Integer>();
v.setSize(number.length);
v[0] = number[0];
for(int i=1, vSize = 1; i < number.length; i++, vSize++){
int j = 0, k = 0;
for(j = 0; j < vSize; j++) {
if(v[j] < number[i]) {
break;
}
for(k = vSize; k > j; k--) {
v[k] = v[k -1];
}
v[k] = number[i];
}
for(int s:v)
System.out.print(s + " ");
}
}

Removing elements in ArrayList with certain size

So I'm currently stuck on one of my programs. I have to use a for loop to remove all elements in the ArrayList that's greater than 4. I have no idea how to do this. Someone help?
Here is my code:
import java.util.ArrayList;
public class P4E
{
public static void main( String args[] )
{
ArrayList<String> universities = new ArrayList<String>();
universities.add("Princeton");
universities.add("UCSD");
universities.add("UCLA");
universities.add("SDSU");
universities.add("UCI");
int size = universities.size();
System.out.println( "The size of the ArrayList is: " + size);
System.out.println("");
System.out.println("Now using a for-each loop");
System.out.println("");
for (String a : universities)
{
System.out.println( a );
}//end for-each
System.out.println( "" );
System.out.println( "Now using a for loop" );
System.out.println( "" );
for (int i = 0; i < universities.size(); i++)
{
if (universities.get(i) > 4 )
{
universities.remove(i);
}//end if-statement
}//end for loop
}//end main
}
Use the Java 8 removeIf default method on List:
universities.removeIf(u -> u.length() > 4);
If you're trying to remove elements that are larger than 4 characters, this will work:
for (int i = 0; i < universities.size(); i++)
{
System.out.println("checking "+universities.get(i));
if (universities.get(i).length() > 4 )
{
universities.remove(i);
i--;
}//end if-statement
}//end for loop
The .length() is necessary because you're interested in the size of the string, you can't compare a string to an int directly. The i--; ensures that you don't skip over elements as the size of the list changes.
The System.out.println is to show you what elements it is checking, so you can see that you're walking through the list correctly.
Instead of if(universities.get(i) > 4)
Do this:
for (int i = size; i >= 0; i--)
{
if (i > 4 )
universities.remove(i); // Shorten to only 1 line instead of brackets
}//end for loop
Reason
You want to remove the university at point i in this case, not get the university object. When you call universities.get(i) it returns the Object in the universities ArrayList
With current code problem is in the fact that you increasing i every time, while sometimes you remove element which cause indexes for elements to the left to be shifted. This results in the fact that you skip check for some elements. Example:
Initial array: [5,6,7], index i = 0.
You removed element at index 0 - array became [6,7], then you increase i and it became 1 - so index for next element to check is 1 and this reffer to 7, not to 6. Result - you didn't check 6.
Solutions:
1) Add i--; immediately after universities.remove(i);
2) Iterate in descending order: for (int i = 0; i < universities.size(); i++) -> for (int i = universities.size() - 1; i >= 0; i--)
3) Use java streams:
universities = universities.stream()
.filter(e -> e > 4)
.collect(Collectors.toList());
The problem with your code is that i advances to the next value even after a remove() call, meaning that your code will skip some items.
Here is what's happening: let's say you want to delete all numbers 4 and above from this list: {9 5 7 4 3}. When i is zero, your algorithm will find 9 and remove it. Now i is 1, but your list is {5 7 4 3}, so 5 at index zero will never be checked again.
One solution is to iterate in reverse order:
for (int i = universities.size() - 1; i >= 0 ; i--) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
}//end if-statement
}//end for loop
Now that i goes down instead of going up it does not matter whether the list shrinks or not.
What exactly do you mean by greater than 4? If you meant length of the String greater than 4, then here is the solution. Replace the 2nd for loop with the code below.
int i = 0;
while(i<universities.size()) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
} else {
i++;
}
}

Arrays and Array lists

I have most of my program done, but it's not working as I need it to and I've stared at it for a while and I just can't figure it out. Can someone help me notice what I am doing wrong? I'm not asking for someone to just fix it, but explain it.
Create a class called CustomerLister1 with a main method that instantiates an array of String objects called customerName. The
array should have room for seven String objects. Assign each of the
following Strings to sequential locations in the array beginning at
array index 0.
Chris
Lois
Meg
Peter
Stewie
Write an enhanced for loop to display the array of names. What is displayed for the last two array elements? Why is it that value?
Add the Strings “Meg” and “Brian” into index 3, and 4, respectively, so that the array contains the following elements:
Chris
Lois
Meg
Meg
Brian
Peter
Stewie
Write an enhanced for loop to display the array of names.
Write a second, traditional for loop that checks each element for the String “Meg”, if found in the array, remove it, shift the
remaining elements, and display the array of names. Are both
instances of “Meg” removed correctly from the array?
Modify the code you wrote part #1 for a second class called CustomerLister2 so that you are using an ArrayList instead of an array
to store the names as String objects.
Add the five names as done previously, and then add “Brian” so that it
is the 4th name in the ArrayList. Now add “Meg” into the third
position in the list (there will be two identical strings “Meg” in the
list).
Use an enhanced for loop for displaying all the String objects, as in
part #1 and use a traditional for loop to remove “Meg” and show the
revised ArrayList. Once again, was “Meg” removed completely from the
list?
public class CustomerLister1
{
public static void main(String[] args)
{
String[] customerName = new String[7];
customerName[0] = "Chris";
customerName[1] = "Lois";
customerName[2] = "Meg";
customerName[3] = "Peter";
customerName[4] = "Stewie";
for (int i = customerName.length-1; i > 3; i--)
{
customerName[i] = customerName[i - 2];
}
customerName[3] = "Meg";
customerName[4] = "Brian";
for (int m = 0; m <= customerName.length-1; m++)
{
if (customerName[m].equals("Meg"))
{
for (int j = m; j < customerName.length; j++)
{
if (j < customerName.length-2)
{
customerName[j] = customerName[j+1];
} else {
customerName[j] = "";
}
}
m++;
}
for (String element : customerName)
{
System.out.println(element);
}
}
}
}
First, an "enhanced" loop (I think) is a for-each loop. It looks like this in java.
for (String name : customerName) {
System.out.println(name);
}
whereas a traditional for loop is iteration, which is what you were doing.
for (int i = 0; i < num; i++) {
System.out.println(customerName[i]);
}
You are printing too much output mainly because your last for loop is embedded too far. Let me increase the tab stops to make it more obvious.
for (int m = 0; m <= customerName.length-1; m++)
{
if (customerName[m].equals("Meg"))
{
for (int j = m; j < customerName.length; j++)
{
if (j < customerName.length-2)
{
customerName[j] = customerName[j+1];
} else {
customerName[j] = "";
}
}
m++;
}
for (String element : customerName)
{
System.out.println(element);
}
}
Do you see how your last for loop is embedded into your first one? This means everytime the first for loop for (int m = 0; m <= customerName.length-1; m++) runs, the last for loop is ALSO going to run. Which means the entire loop runs m times.
For a mathematical explanation, LoopA runs n times. LoopB runs m times. If LoopB is inside of LoopA then LoopB will run a total of n * m times.
The problem with many outputs is obvious as you have your printing loop inside your "enhanced" loop, whatever that means.
Other than that you should use <= in your innermost loop as you also want to move the very last element.

How can I overwrite the localities in an array?

I have a Method that uses an array with certain numbers of localities. I ask for the user to input the number of the locality to start overwriting. My object array has other objects in the localities. I'm not sure if what I am doing with the j variable is correct. The following is my way of moving the objects in the localities.
public void overwriteArray(int number) {
for (int i=0; i<array.length ; i++) {
if (i ==(number-1) {
for(int j = i ; j<array.length ; j++) {
array[j] = array[j+1];
}
}
}
}
Is there any other way? Is there something easier than using arrays? Is correct the way of moving the objects in the array?
Thanks!
If I understand well: you want to remove the element described by the number input parameter, and shift the remaining elements to the left. Also, the element on the end stays in place with all moves. And number is the number of the element to be removed, so 1 means, the 1st element is to be removed, meaning the **index of 0 **in the array, am I right?
Then your code seems to work OK, with the exception, that you don't need the outer loop. At all:
public void overwriteArray(int number) {
for(int j = number-1 ; j<array.length ; j++) {
array[j] = array[j+1];
}
}
The outer loop didnát do anzthing, except when the loop variable was exactlz number-1 Also beware, this might address outside of the array length...
You could use System.arraycopy for this purpose too, no need for this lenghty code (the indexes might be off, and of course it would dies with improper number argument):
public void overwriteArray(int number) {
System.arraycopy(array, number, array, number-1, array.length-number);
}
It does work on the same src and dest:
If the src and dest arguments refer to the same array object, then the copying is performed as if the components at positions srcPos through srcPos+length-1 were first copied to a temporary array with length components and then the contents of the temporary array were copied into positions destPos through destPos+length-1 of the destination array.
Your outer loop iterates over the hole array and execute the inner loop only when i == (number-1) so array.length - 1 of the iterations are for free!
You could get rid of the outer for loop along with the if (i ==(number-1) { and instead write for(int j = (number-1) ; j<array.length ; j++) {
also array[j+1]; will give you an ArrayIndexOutOfBoundsException on the last iteration. Maybe you will want to go only till array.length - 1.
Also to avoid ArrayIndexOutOfBoundsException you should make sure that number is within the range [1, array.length] before iteration.
Here is how could looks like:
#Test
public void overwritearrayay() {
String[] array = {"foo", "bar", "foobar"};
log(Arrays.toString(array));
overwritearrayay(array, 1);
log(Arrays.toString(array));
}
public void overwritearrayay(String[] array, int n) {
if(n > 0 && array != null && n <= array.length) {
for(int i = n-1; i < array.length-1; i++) {
array[i] = array[i+1];
}
array[array.length-1] = null;
}
}
public static void log(String string) {
System.out.println(string);
}
Output
[foo, bar, foobar]
[bar, foobar, null]

Categories

Resources