java.lang.IllegalStateException in iterator.remove() - java

Rocket class contains: canCarry(Item item)>checks if this item can be carried/ carry updates the weight with total weight.
U2 class is child of Rocket contains: currentweight, maxWeight=18 tons
Item class contains: name to be shipped & weight.
In the method loadU2 I am trying to access a list of items and adding it into one rocket until maxWeight of that rocket is reached . For example I have 216 tons of items to carry returning a list of 12 ships.
It throws me java.lang.IllegalStateException error in the line iterator.remove(). I do not know how to go about it, but it looks like it is not allowing me to remove the items while iterating.
public ArrayList<Rocket> loadU2(ArrayList<Item> loadItems){
//list of ships
ArrayList<Rocket> U2Ships = new ArrayList<Rocket>();
for(Iterator<Item> iterator = loadItems.iterator(); iterator.hasNext();) {
//create a new ship
Rocket tempShip = new U2();
Item tempItem = iterator.next();
//loop over items check if it can be filled then remove the item that was filled.
while(tempShip.currentWeight<tempShip.weightLimit) {
if(tempShip.canCarry(tempItem)){
tempShip.carry(tempItem);
iterator.remove();
}
}
U2Ships.add(tempShip);
}
return U2Ships;
}
Exception in thread "main" java.lang.IllegalStateException
at java.base/java.util.ArrayList$Itr.remove(ArrayList.java:980)
at Simulation.loadU1(Simulation.java:35)
at Main.main(Main.java:14)
Simplified example of what the code is doing:
Assuming maxWeight for each ship = 11 tons
ArrayList loadItems = [3,5,5,8,1,2,3,5] tons
- Ship[1]=[3,5,1,2]
- new list to iterate over >> [5,8,3,5]
- Ship[2]=[5,3]
- new list to iterate over >> [8,5]
- Ship[3]=[8]
- new list to iterate over >> [5]
- Ship[4]=[5]

Please, rewrite your code by creating new ArrayList, instead of changing the existing list inside its own iterator:
public ArrayList<Rocket> loadU2(ArrayList<Item> loadItems){
//list of ships
ArrayList<Rocket> U2Ships = new ArrayList<Rocket>();
ArrayList<Item> updatedLoadItems = new ArrayList<Item>();
for(Iterator<Item> iterator = loadItems.iterator(); iterator.hasNext();) {
//create a new ship
Rocket tempShip = new U2();
Item tempItem = iterator.next();
//loop over items check if it can be filled then only leave the load item that was not fully filled.
boolean addLoadItem = true;
while(tempShip.currentWeight<tempShip.weightLimit) {
if(tempShip.canCarry(tempItem)){
tempShip.carry(tempItem);
addLoadItem = false;
}
}
if (addLoadItem) {
updatedLoadItems.add(tempItem);
};
U2Ships.add(tempShip);
}
loadItems.removeAll();
loadItems.addAll(updatedLoadItems);
return U2Ships;
}
This is not the best solution, but to provide a better solution, you need to change the signature of public ArrayList<Rocket> loadU2(ArrayList<Item> loadItems)
You can try to improve your code by refactoring it.
Hint: right now your loadU2 method tries to do both things at the same time: change loadItems and create U2Ships. This is a direct violation of the single responsibility principle. Try to imagine the soldier who would try to shoot the gun and throw grenade at the same time! One thing at the time.

The problem is here:
while(tempShip.currentWeight<tempShip.weightLimit) {
if(tempShip.canCarry(tempItem)){
tempShip.carry(tempItem);
iterator.remove();
}
}
You are calling iterator.remove() within a loop. If the condition tempShip.canCarry(tempItem) holds twice, you call iterator.remove() twice, and this is not allowed (the second time, the item is already removed).
I don't know how the method canCarry is implemented, but note that if it is the case that tempShip.currentWeight<tempShip.weightLimit is true, but tempShip.canCarry(tempItem) is false, your loop will run forever.

use listIterator instead of Iterator.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
like used here.
Remove elements from collection while iterating

public ArrayList<Rocket> loadU2(ArrayList<Item> loadItems){
//list of ships
int shipNum=0;
int itemsloaded=0;
ArrayList<Rocket> U2Ships = new ArrayList<Rocket>();
while(!loadItems.isEmpty()) {
System.out.println("number of ships created: "+shipNum++);
//create a new ship
Rocket tempShip = new U2();
//loop over items check if it can be filled then only leave the load item that was not fully filled.
while(iterator.hasNext()) {
Item tempItem = iterator.next();
if(tempShip.canCarry(tempItem)){
System.out.println("number of items loaded: "+(itemsloaded++));
tempShip.carry(tempItem);
iterator.remove();
}
}
U2Ships.add(tempShip);
}
return U2Ships;
}
Thank you guys for the help, this should fix 2 problems: infinity, and the iterator.remove().

Related

why is everything getting removed from my arraylist?

I'm trying to answer this question:
Program the method findIngredients. This method takes in a String called
foodInStock, and an ArrayList of Strings called ingredients. The method should return an
ArrayList of ingredients that were not found in foodInStock.
for example if:
foodInStock = “tomatopotatocornturkeycarrotstuffing”
ingredients = {“potato”, “corn”, “salt”, “chicken”, “turkey”}
returns {“salt”, “chicken”}
I tried writing some code but for some reason everything is getting removed when I use the above example on my program. Where did my program go wrong?
Here's my code:
public static ArrayList<String> findIngredients(String foodInStock, ArrayList<String> ingredients){
ArrayList<String> ingredientsNotFound = new ArrayList<String>();
int i = 0;
for (; i < ingredients.size(); i++) {
for (int x = 0; x < foodInStock.length()-(ingredients.get(i).length())+1; x++) {
if (ingredients.get(i) == foodInStock.substring(x, (x + ingredients.get(i).length()))) {
ingredients.remove(i);
i = 0;
break;
}
}
}
ingredients = ingredientsNotFound;
return ingredientsNotFound;
}
I think there are two main things to cover here.
First, the way to build the final result. You are currently removing items from the original input; a better strategy is to add items to a new list (partially because it's simpler to think about and partially because you generally don't want to modify a list while iterating over it).
You also are, probably accidentally, overwriting your list with an empty list at the end.
Second, the way to determine whether or not the ingredient is in the string input. Rather than looping over the whole string and inspecting substrings, you can instead use the indexOf() method to see whether or not the string includes the current item.
public static ArrayList<String> findIngredients(String foodInStock, ArrayList<String> ingredients) {
ArrayList<String> results = new ArrayList<>();
for (String ingredient : ingredients) {
if (foodInStock.indexOf(ingredient) == -1) {
results.add(ingredient);
}
}
return results;
}
Here we initialize a new list for the results. We then loop over every individual ingredient in the input list, and ask whether or not that ingredient is present in the string input. When it is not (indexOf() returns -1), we add it to the results list. At the end, the results contains every ingredient not found.

stuck with a malfunctioning while loop

I'm stuck with this problem in home work for a long time now, would appreciate your professional help.
I need to simulate filling up rockets destined for a "mission to Mars" with all kinds of items arranged already in an array list. Maximum weight of each rocket including cargo is a given (18,000 Kg), as well as rocket net weight (10,000 Kg) and each item's weight (Item object includes fields "weight" and "itemType", such as "building material", "water", etc,.)
Instructions are to fill each rocket until it's fully loaded and only then create another one. It seems that my fellow students ignored this instruction so their code cannot help.
Sorting the array in ascending / descending order according to weight does not solve the problem.
My problem is that although I have used while loops all over, rockets refuse to fill up despite still having space left that can be filled with an item still left on the list. Loop won't skip the next 2-3 items (the most I managed to get is skipping one item) and find the one item that can still be loaded.
Bellow is also the list of items.
public ArrayList<U1> loadU1(ArrayList<Item> items) {
ArrayList<U1> fleetU1 = new ArrayList();
int i = 0;
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
U1 rocketU1 = new U1(); // create new rocket with zero cargo
while (rocketU1.canCarry(items.get(i))) { // "canCarry" checks if item's weight fits in:
/* public final boolean canCarry(Item cargo){
if(currentRocketWeight + cargo.weight <= maxRocketWeight){
return true;
} else {
return false; }} */
rocketU1.carry(items.get(i));
// "carry" updates rocket total weight - no more than 18000 Kg including rocket net weight 10000 Kg, i.e. max cargo weight is 8000 Kg:
/* public final int carry(Item cargo){
currentRocketWeight += cargo.weight;
return currentRocketWeight;}
*/
items.remove(i); // remove loaded item from list
}
fleetU1.add(rocketU1); // add rocket to fleet
}
return fleetU1;
}
/*arraylist "items" - "phase-1.txt":
building tools=2000
building tools=2000
building tools=2000
building tools=5000
building tools=5000
building tools=2000
building tools=1000
building tools=5000
building tools=6000
shelter equipment=5000
construction equipment=5000
plants=1000
steel=8000
books=1000
water=5000*/
public ArrayList<Item> loadItems(int phaseNum) {
try {
switch (phaseNum) {
case 1:
out.println("Loading phase 1:");
fileName = "phase-1.txt";
break;
case 2:
out.println("Loading phase 2:");
fileName = "phase-2.txt";
break;
default:
out.println("argument must be 1 or 2");
}
File file = new File(fileName);
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
lineFromFile = scanner.nextLine();
String[] list = lineFromFile.split("=");
Item item = new Item(); //(list[0], );
item.itemType = list[0];
item.weight = Integer.parseInt(list[1]);
itemList.add(item); // create ArrayList of items
}
scanner.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return itemList;
}
You need to iterate the items per rocket.
// create a copy of the items so we can remove without harm to the caller
List<Items> ourList = new ArrayList<>(items);
// you should probably sort the list by weight, descending order
while (!ourList.isEmpty()) {
// we need a rocket.
U1 rocket = new U1();
// go through all the items and load if item fits
Iterator<Item> iterator = ourList.iterator();
while (iterator.hasNext()) {
// the next() call that's mentioned in the comment
Item item = iterator.next();
if (rocket.canCarry(item)) {
rocket.carry(item);
// you need to remove from the iterator itself, not from the list
// or you will get an exception because that makes the iterator invalid
// it will remove from the underlying list as well though
iterator.remove();
}
}
fleet.add(rocket);
}
You need to have one method to fill a rocket and then call that method in a loop while there is still cargo left to load. To make this work without recursive calls I have changed the array of rockets, `fleet', to be a class member
private List<Item> loadRocket(List<Item> items) {
Iterator<Item> iterator = items.iterator();
List<Item> loaded = new ArrayList<>();
U1 rocketU1 = new U1();
while (iterator.hasNext()) {
Item item = iterator.next();
if (rocketU1.canCarry(item)) {
rocketU1.carry(item);
loaded.add(item);
}
}
items.removeAll(loaded);
fleetU1.add(rocketU1);
return items;
}
And then call it in a loop
while (!items.isEmpty()) {
items = loadRocket(items);
}
If you don't want to have the ´fleetas a class member you could move the creation of the rocket and adding to thefleetlist to outside of ´loadRocket and instead send it as a parameter.
while (!items.isEmpty()) {
U1 rocket = new U1();
items = loadRocket(rocket, items);
fleet.add(rocket);
}
So here what I would do:
For each item, I would try to place it in a rocket already in the fleet; if not possible, I would add a new rocket to the fleet. If the item cannot fit even in an empty rocket, it is kept in the input list, otherwise, it is removed:
public List<U1> loadU1(List<Item> items) {
List<U1> fleetU1 = new ArrayList<>();
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
Item item = iterator.next();
U1 rocketU1 = null;
for (U1 u1: fleetU1) {
if (u1.canCarry(item)) {
rocketU1 = u1;
break;
}
}
if (rocketU1 == null) {
rocketU1 = new U1();
if (!rocketU1.canCarry(item)) {
// the item is too heavy
continue;
}
fleetU1.add(rocketU1);
}
rocketU1.carry(item);
iterator.remove();
}
return fleetU1;
}

Llist of items that have been deleted

I want to display the list of items that i have deleted so far (JAVA). I'm using database as mysql. I'm giving a button in UI on clicking it web service will called and should display the deleted entry. I tried this:
for (Iterator<String> iter = list.listIterator(); iter.hasNext(); ) {
String a = iter.next();
if (...) {
iter.remove();
}
}
You can store all the removed items in a data structure like linked list which is appropriate for appending new items and you don't need to consist memory to it when you define it.
List<String> listOfRemovedItems = new LinkedList<>();
Iterator iterator = originalList.iterator();
while (iterator.hasNext()) {
String s = iterator.next().toString();
if ("meets your condition") {
listOfRemovedItems.add(s);
iterator.remove();
}
}
I added one extra column in table as Flag. Now when user delete it flag is to 1 otherwise it is false.

Writing a method with ArrayList of strings as parameters

I am trying to write a method that takes an ArrayList of Strings as a parameter and that places a string of four asterisks in front of every string of length 4.
However, in my code, I am getting an error in the way I constructed my method.
Here is my mark length class
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
And the following is my main class:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
ish.marklength4(words);
}
}
Essentially in this case, it should run so it adds an arraylist with a string of "****" placed before every previous element of the array list because the lengths of the strings are all 4.
BTW
This consists of adding another element
I am not sure where I went wrong. Possibly in my for loop?
I got the following error:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at Marklength.marklength4(Marklength.java:7)
at MarklengthTestDrive.main(MarklengthTestDrive.java:18)
Thank you very much. Help is appreciated.
Let's think about this piece of code, and pretend like you don't get that exception:
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
Ok, so what happens if your list just contains item.
You hit the line if(n.length() ==4){, which is true because you are looking at item, so you go execute its block.
Next you hit the line themarklength.add("****");. Your list now has the element **** at the end of it.
The loop continues, and you get the next item in the list, which happens to be the one you just added, ****.
The next line you hit is if(n.length() ==4){. This is true, so you execute its block.
You go to the line themarklength.add("****");, and add **** to the end of the list.
Do we see a bad pattern here? Yes, yes we do.
The Java runtime environment also knows that this is bad, which is why it prevents something called Concurrent Modification. In your case, this means you cannot modify a list while you are iterating over it, which is what that for loop does.
My best guess as to what you are trying to do is something like this:
import java.util.ArrayList;
public class Marklength {
ArrayList<String> marklength4(ArrayList <String> themarklength){
ArrayList<String> markedStrings = new ArrayList<String>(themarklength.size());
for(String n : themarklength){
if(n.length() ==4){
markedStrings.add("****");
}
markedStrings.add(n);
}
System.out.println(themarklength);
return markedStrings;
}
}
And then:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
words = ish.marklength4(words);
}
}
This...
if(n.length() ==4){
themarklength.add("****");
}
Is simply trying to add "****" to the end of the list. This fails because the Iterator used by the for-each loop won't allow changes to occur to the underlying List while it's been iterated.
You could create a copy of the List first...
List<String> values = new ArrayList<String>(themarklength);
Or convert it to an array of String
String[] values = themarklength.toArray(new String[themarklength.size()]);
And uses these as you iteration points...
for (String value : values) {
Next, you need to be able to insert a new element into the ArrayList at a specific point. To do this, you will need to know the original index of the value you are working with...
if (value.length() == 4) {
int index = themarklength.indexOf(value);
And then add a new value at the required location...
themarklength.add(index, "****");
This will add the "****" at the index point, pushing all the other entries down
Updated
As has, correctly, been pointed out to me, the use of themarklength.indexOf(value) won't take into account the use case where the themarklength list contains two elements of the same value, which would return the wrong index.
I also wasn't focusing on performance as a major requirement for the providing a possible solution.
Updated...
As pointed out by JohnGarnder and AnthonyAccioly, you could use for-loop instead of a for-each which would allow you to dispense with the themarklength.indexOf(value)
This will remove the risk of duplicate values messing up the index location and improve the overall performance, as you don't need to create a second iterator...
// This assumes you're using the ArrayList as the copy...
for (int index = 0; index < themarklength.size(); index++) {
String value = themarklength.get(index);
if (value.length() == 4) {
themarklength.add(index, "****");
index++;
But which you use is up to you...
The problem is that in your method, you didn't modify each string in the arraylist, but only adds 4 stars to the list. So the correct way to do this is, you need to modify each element of the arraylist and replace the old string with the new one:
void marklength4(ArrayList<String> themarklength){
int index = 0;
for(String n : themarklength){
if(n.length() ==4){
n = "****" + n;
}
themarklength.set(index++, n);
}
System.out.println(themarklength);
}
If this is not what you want but you want to add a new string "**" before each element in the arraylist, then you can use listIterator method in the ArrayList to add new additional element before EACH string if the length is 4.
ListIterator<String> it = themarklength.listIterator();
while(it.hasNext()) {
String name = it.next();
if(name.length() == 4) {
it.previous();
it.add("****");
it.next();
}
}
The difference is: ListIterator allows you to modify the list when iterating through it and also allows you to go backward in the list.
I would use a ListIterator instead of a for each, listiterator.add likely do exactly what you want.
public void marklength4(List<String> themarklength){
final ListIterator<String> lit =
themarklength.listIterator(themarklength.size());
boolean shouldInsert = false;
while(lit.hasPrevious()) {
if (shouldInsert) {
lit.add("****");
lit.previous();
shouldInsert = false;
}
final String n = lit.previous();
shouldInsert = (n.length() == 4);
}
if (shouldInsert) {
lit.add("****");
}
}
Working example
Oh I remember this lovely error from the good old days. The problem is that your ArrayList isn't completely populated by the time the array element is to be accessed. Think of it, you create the object and then immediately start looping it. The object hence, has to populate itself with the values as the loop is going to be running.
The simple way to solve this is to pre-populate your ArrayList.
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>() {{
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
}};
}
}
Do tell me if that fixes it. You can also use a static initializer.
make temporary arraylist, modify this list and copy its content at the end to the original list
import java.util.ArrayList;
public class MarkLength {
void marklength4(ArrayList <String> themarklength){
ArrayList<String> temp = new ArrayList<String>();
for(String n : themarklength){
if(n.length() ==4){
temp.add(n);
temp.add("****");
}
}
themarklength.clear();
themarklength.addAll(temp);
System.out.println(themarklength);
}
}

How to dynamically prune Java List when Iterator does not support remove()?

I have the following code:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
iter.remove();
}
When I run this code I get a runtime java.lang.UnsupportedOperationExceptionError with a vague exception message of null that gets thrown on the iter.remove() line. It seems that some Java Iterators don't support the remove method and will throw this exception.
I can't change the widgetService.getAllWidgets() method to return a List<Widget> and am stuck with the Widget[] array return value.
So I ask: what can I do to loop through my widgets array and dynamically prune out ones that are named "Melvin"?
If you can afford it, just make a mutable copy of the list. Replace
List<Widget> widgets = Arrays.asList(widgetArray);
with
List<Widget> widgets = new ArrayList<Widget>(Arrays.asList(widgetArray));
Just defer removal until the iterator is done:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
List<Widget> toRemove = new ArrayList<Widget>();
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
toRemove.add(w);
}
widgets.removeAll(toRemove);
Alternatively, just build the list from eligible widgets using the inverse logic:
List<Widget> widgets = new ArrayList<Widget>();
// Add all Widgets not named "Melvin"
for (Widget w : widgetService.getAllWidgets()) {
if(!"Melvin".equals(w.getName()))
widgets.add(w);
}
The asList() list is still backed by the array.
You may want to loop through each element of the array and add it to a brand new list. This would take two loops.
Or better yet, compare the string value and then add to the list. This way, you have one loop and a brand new list.
With guava you can get rid of all that code and let a fast, well tested library take care of it for you.
Collection<Widget> noMelvins = Collections2.filter( Arrays.asList(widgetArray), new Predicate<Widget>() {
#Override public boolean apply( final Widget arg) {
return !"Melvin".equals(arg.getName());
}
});

Categories

Resources