This question already has answers here:
Calling remove in foreach loop in Java [duplicate]
(11 answers)
Closed 4 years ago.
I've been trying so far to write a method ,removeEvenLength that takes an ArrayList of Strings as a parameter and that removes all of the strings of even length from the list. But so far I've been getting a IndexOutOfBoundsException and I don't know why.
Any help would be appreciated
public static ArrayList<String> removeEvenLength(ArrayList<String> list) {
int size = list.size();
ArrayList<String> newLst = new ArrayList<String>();
for (int x = 0; x < size; x++) {
if (list.get(x).length() % 2 == 0) {
list.remove(x);
}
}
return list;
}
Once you remove an element, the size of the list reduces by one and hence the variable size no longer denotes the true size of the list
Also, after you remove a String at index i, the elements from i+1, i+2.. list.size() - 1 will be moved to the left by one position. So, incrementing the loop counter x all the time is wrong and you will skip some elements.
Here's a way to do it right
for (int x = 0; x < list.size();) {
if (list.get(x).length() % 2 == 0) {
list.remove(x);
} else {
x++;
}
}
public static List<String> removeEvenLength(List<String> list) {
List<String> newList = new ArrayList<String>();
for (String item: list) {
if (item.length % 2 != 0) {
newList.add(item);
}
}
return newList;
}
Related
This question already has an answer here:
What is a StringIndexOutOfBoundsException? How can I fix it?
(1 answer)
Closed 2 years ago.
I have a homework ,and which I have tried to solve for serveral hours.
Description:we have 2 sorted array list ,the length of list a is n, the length of list b is m;we assume a and b are already sorted, and that the lists a and b do not contain duplicates.And I thought as following ,but I always get a error:index out of bound .
public static List<Integer> union(List<Integer> list1, List<Integer> list2 ) {
List<Integer> union = new ArrayList<>();
int n1 = 0;
int n2 = 0;
for (int i = 0; i < list1+list2; i++) {
....
}
If you loop till m + n one of the array which might have a lesser size than the other will get exhausted and hence the array index out of bound. Instead you can use a while loop which will loop only till the minimum size of the two lists and later, since the list is sorted you can add all the remaining elements from the bigger of the two lists into the union
// iterate till the minimum of two lists
while(index1 < a.size() && index2 < b.size()) {
if (a.get( index1 ) > b.get( index2 )) {
union.add(i++, b.get( index2++ ));
}
else {
union.add(i++, a.get( index1++ ));
}
}
//add the elements of the bigger list to the union
while(index1 < a.size()) {
union.add(i++ ,a.get( index1++ ));
}
while(index2 < b.size()){
union.add(i++, b.get( index2++ ));
}
public static List<Integer> union(List<Integer> one, List<Integer> two) {
List<Integer> res = new ArrayList<>(one.size() + two.size());
for (int i = 0, a1 = 0, a2 = 0; i < one.size() + two.size(); i++) {
if (a1 == one.size())
res.add(two.get(a2++));
else if (a2 == two.size())
res.add(one.get(a1++));
else
res.add(one.get(a1) <= two.get(a2) ? one.get(a1++) : two.get(a2++));
}
return res;
}
This question already has answers here:
ArrayList.remove is not working in a loop
(7 answers)
Closed 2 years ago.
I'm trying to remove even-length words from a String ArrayList, and it's almost working, except that for some reason one even-numbered word is getting through.
My code:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
I must be missing something, but I'm failing to determine what exactly. Pointers much appreciated.
The issue is that you are modifying the ArrayList while iterating over it, which changes its size. You need to decrease the index by one each time you remove an element since that index will now refer to the next element.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
i--;
}
}
return a;
}
Looping backwards will also fix this problem, as elements will never be shifted to a position that you have yet to check.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
for (int i = a.size() - 1; i >= 0; i--) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
}
You can also use List#removeIf with Java 8 and up to accomplish this easier.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
a.removeIf(str -> str.length() % 2 == 0);//or str.length() & 1 == 0
return a;
}
You can use Stream#filter to construct a new List with the odd-length Strings without modifying the old one.
public static ArrayList < String > removeEvenLength(ArrayList < String > a) {
return a.stream().filter(str -> str.length() % 2 == 1).collect(Collectors.toCollection(ArrayList::new));
}
Your code is working fine but as you will delete any element from a particular index then the list index will change means the immediate element after the removed element will be updated to removed element.
You can modify your code to below code:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = 0; i < a.size(); i++) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
i-=1;
}
}
return a;
}
The issue here is that when you do a.remove(i);, the ArrayList automatically updates its indices, so you end up skipping a value. Here's an example of how this could happen:
You get the 1st element (a.get(0);)
You find that it is an even length string (wordEntry.length() % 2 == 0)
You remove it (a.remove(i);)
Your for loop advances to the next value (now i = 1)
You get what is now the 2nd element (a.get(1);), but because you removed what was the 1st element, this is now what used to be the 3rd element.
In this scenario, you skipped over that 2nd element, which is why some strings are slipping through unnoticed. This is where I would suggest using the enhanced for loop, which simplifies things significantly so you don't need to worry about what index you are on. It would look something like this:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (String wordEntry : a) {
if (wordEntry.length() % 2 == 0) {
a.remove(wordEntry);
}
}
return a;
}
Alternatively, if you want to go for an even simpler one-liner, ArrayList offers some very convenient methods for manipulating ArrayLists. One such method, called removeIf(), pretty much does exactly what you want, but I think it's good to learn to properly use for loops before going on to use built-in methods like this. That said, if you wanted to go that route, here's how I would do it:
public ArrayList<String> removeEvenLength(ArrayList<String> a) {
a.removeIf((wordEntry) -> wordEntry.length() % 2 == 0);
return a;
}
Also, I just felt that I should note that there are other ways of finding even numbers. You can also use (wordEntry.length() & 1) == 0. There's no real difference as far as performance or anything, it's really just personal preference, but I just felt I should mention another way of doing it :)
Your method gets out of sync with the elements when removing from front to back. So remove them in reverse order.
ArrayList<String> words = new ArrayList<>(List.of("abc", "efgh", "o", "pq", "rs"));
words = removeEvenLength(words);
System.out.println(words);
public static ArrayList<String> removeEvenLength(ArrayList<String> a) {
for (int i = a.size()-1; i >= 0; i--) {
String wordEntry = a.get(i);
if (wordEntry.length() % 2 == 0) {
a.remove(i);
}
}
return a;
}
And as stated you can also use removeIf()
Explanation.
As you move forward, removing elements, your index is still incrementing normally to get to the next element. But the list has changed by removing previous elements so the index may skip over elements that need to be checked.
Assume you want to remove even elements.
consider a = [5,20,40], index = 1
remove a[index++]; the list is now [5,40] and index = 2. 40 will not be checked because the list is now of size 2 and the iteration will cease.
By removing them in reverse, the decrease in the length of the list does not impact the index.
again consider a = [5,20,40], index = 2
remove a[index--]; the list is now [5,20] and index = 1. 20 will be checked and removed. Index will then be 0 and one element will remain.
This behavior can be mitigated by adjusting the index when removing items. However, by removing in reverse order, no such adjustment is required.
Remove while iterating makes the problem. You can use removeIf for this
a.removeIf(wordEntry -> (wordEntry.length() % 2 == 0));
or use ListIterator to iterate the arraylist
ListIterator<String> iter = a.listIterator();
while(iter.hasNext()){
if(iter.next().length() % 2 == 0){
iter.remove();
}
}
I have a problem comparing the numbers inside a list so that it does not repeat using Random. I wanted the numbers to be random, but only those that are not on the list can be added.
Here is my code:
private void addToListNumber() {
int randomPosition = new Random().nextInt(5);
int maxPosition = 5;
if (list.size() < 1) {
list.add(1);
addToListNumber();
} else if (list.size() < maxPosition) {
for (Integer integer : list) {
if (integer == randomPosition) {
addToListNumber();
}
}
list.add(randomPosition);
addToListNumber();
} else {
for (Integer integer : list) {
System.out.println(integer);
}
}
}
The numbers are repeated.
If I understand correctly your question, you're trying to build a list of 5 integers, in a random order.
A simple way to get there is to generate a list of ordered numbers and then randomly swap them.
First create your list of ordered numbers.
public List<Integer> createOrderedList(int size) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(i);
}
return list;
}
Then create a method that swaps two elements in the list, given their indexes.
public void swap(List<Integer> list, int i, int j) {
Integer hold = list.get(i);
list.set(i, list.get(j));
list.set(j, hold);
}
Last, create a method that mixes all of them.
public void mix(List<Integer> list) {
Random random = new Random();
for (int i = 0; i < list.size(); i++) {
swap(list, i, random.nextInt(list.size()));
}
}
Call the methods in this order:
List<Integer> list = createOrderedList(5);
mix(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
This is happening because of the recursive call addToListNumber(). To make it simple and short let me assume maxPosition = 3
At first you add 1 to the list. Then suppose randomPosition = 2 and you add 2 to the list and call addToListNumber(). Now suppose randomPosition = 1, then you call addToListNumber() again on comparing integer == randomPosition (you have compared only the first element here and it matched)
Now let randomPosition = 3 and 3 be inserted. Since, this is a recursive call, on returning to addToListNumber() which was invoked for randomPosition = 1 (in the previous case), the list is not over (the list has {1,2,3}) here and only the first element has been compared. At the end of the loop it inserts 1 again and your list becomes {1,2,3,1} hence duplicates.
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).
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 6 years ago.
How do I reset a for-loop while looping through a list? For example, I want the for-loop to reset i.e. (i=0 and j=1) when there are duplicates in a list .
In this piece of code, I want the duplicates removed and the for-loop reset when subsequent entries in the list are equal. For instance, we have
list1 = [east, west, west, east]
I want the resultant list1 to be equal to an empty list.
This is because, When both "west" entries are eliminated, this results in the list updating to [east,east]. Since this is also a duplicate, the result must hence be an empty list [].
j=1;
for (int i=0;i<(list1.size()-1);i++){
if((list1.get(i)==list1.get(j))){
list1.remove(i);
list1.remove(i);
i=0;
j=1;
}else{
j++;
}
}
You can loop through the ArrayList reversely:
ArrayList<String> list1 = new ArrayList<String>(Arrays.asList(new String[]{"east", "west", "west", "east", "foo"}));
for (int i = (list1.size() - 2);i >= 0;i--){
for(int j = (list1.size() - 1);j > i;j--) {
if((list1.get(i).equals(list1.get(j)))) {
list1.remove(i);
list1.remove(i);
}
}
}
System.out.println(list1);
If you want the duplicates removed, why don't you use a Set ?
String[] list1 = {"east", "west", "west", "east"};
List<String> list = new ArrayList<>(Arrays.asList(list1));
Set<Object> alreadyPresent = new HashSet<>();
Iterator<String> iterator = list.iterator();
for (String element : new ArrayList<String>(list)) {
if (!alreadyPresent.add(element)) {
while(list.remove(element));
}
}
Edit (Much better) :
String[] list1 = {"a","b","b","a","d","e","f"};
List<String> list = new ArrayList<>(Arrays.asList(list1));
for (String element : new ArrayList<String>(list)) {
if(Collections.frequency(list, element) > 1) {
while(list.remove(element));
}
}
List<String> list1 = new ArrayList<String>();
list1.add("east");
list1.add("east");
list1.add("west");
list1.add("test");
list1.add("west");
int j=1;
for (int i=0;i<list1.size();i++){
//here you can say if you want exactly two or more
if(Collections.frequency(list1, list1.get(i)) > 1) {
list1.removeAll(Collections.singleton(list1.get(i)));
i=0;
}
}
System.out.println(list1);
Try modularizing your code a little bit more!
// The function you are trying to impliment
void removePairs(List<Object> list) {
while (removePair(list)) {}
}
Let's use a helper method to make our life easier
// Return true if successfully removed a pair
boolean removePair (List<Object> list) {
for(i = 0; i < list.size() - 1; i++) {
// Get the next objects
Object firstObject = list.get(i);
Object secondObject = list.get(i + 1);
if (firstObject.equals(secondObject)) {
list.remove(i);
list.remove(i + 1);
return true;
}
}
return false;
}
One other note, j = 1 should not be where it is. I am referring to variable scope. In your original code, you won't (hopefully) care about j after the for loop completes. But it is still hanging around, waiting to cause bugs when it gets used for something that it shouldn't!
To state the problem: if in the sequence to duplicate values appear [..., a, a, ...] you want to remove them, and recurse.
Most readable would be to do away with j or do int j = i - 1;.
List<String> list = new ArrayList<>();
Collections.addAll(list, "east", "west", "west", "east");
for (int i = 1; i < list.size(); ++i) {
String value = list.get(i);
int priorI = i - 1;
if (value.equals(list.get(priorI))) {
list.remove(priorI);
list.remove(priorI);
// next i will be priorI but at least 1
i = Math.max(0, priorI - 1); // With ++i will be > 0
}
}