stuck with a malfunctioning while loop - java

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

Related

java.lang.IllegalStateException in iterator.remove()

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().

How to search for an Object in ArrayList?

I want search for an object in an arraylist using one of it attribute: String name.
I have printed out Item Found here, and this works just fine.
public static void searchItems() {
// variable declaration
String itemSearch;
// collect value of variable using Scanner class
System.out.println("\t\tSEARCH ITEMS");
System.out.println("Enter item name: ");
itemSearch = input.next();
//search for an item
for (int i=0; i<itemList.size();i++) {
if (itemList.get(i).name.equalsIgnoreCase(itemSearch)) {
System.out.println("\t\t[ITEM FOUND]");
}
}
}
However, I want to notify when the item is not found as well. When I add else to this for loop, the String itemSeacrh gets matched (might not be the exact right term, sorry) with all the objects in the arraylist, and prints out the notification for every object index.
Let me explain. Suppose, objects: book, pen and pencil are stored in the ArrayList itemList in that respective order and, the for loop is modified the following way:
for (int i=0; i<itemList.size();i++) {
if (itemList.get(i).name.equalsIgnoreCase(itemSearch)) {
System.out.println("\t\t[ITEM FOUND]");
}
else {
System.out.println("\t\t[ITEM NOT FOUND]");
}
}
I want to search for the book. When I enter book as the itemSearch the following get printed in the console:
SEARCH ITEMS
Enter item name:
book
[ITEM FOUND]
[ITEM NOT FOUND]
[ITEM NOT FOUND]
As you can see, it checks and prints that the book is not found in other objects, which in not what I exactly had in mind. I want it to print item found or either item not found, not both at the same time.
Thank you. I hope you understand my query.
created the list and the search item:
List<String> list = new ArrayList<String>();
list.add("book");
list.add("pencil");
list.add("note");
String itemToBeSearched = "book"; // taken as example
if(check(list,itemToBeSearched)){
System.out.println("ITEM FOUND");
}
else
{
System.out.println("NOT FOUND");
}
then the item check function is
public static boolean check(List<String> list, String itemToBeSearched){
boolean isItemFound =false;
for(String singleItem: list){
if(singleItem.equalsIgnoreCase(itemToBeSearched)){
isItemFound = true;
return isItemFound;
}
}
return isItemFound;
}
and it's working for me, please try this and let us know :)
The easiest way to do this is to print when you have found the book, and return. This way you will stop iterating once the book is found, and leave the function immediatly:
for (int i=0; i<itemList.size();i++) {
if (itemList.get(i).name.equalsIgnoreCase(itemSearch)) {
System.out.println("\t\t[ITEM FOUND]");
return;
}
}
System.out.println("\t\t[ITEM NOT FOUND]");
This will not allow you to do any further processing of the book after finding, so you may want to store the book in a variable outside the loop, and execute some code in a conditional:
Item item = null;
for (int i=0; i<itemList.size();i++) {
if (itemList.get(i).name.equalsIgnoreCase(itemSearch)) {
item = itemList.get();
break;
}
}
if null != item {
System.out.println("\t\t[ITEM FOUND]");
// do some stuff ...
} else {
System.out.println("\t\t[ITEM NOT FOUND]");
// do some other stuff ...
}
As a final note, look into using for-each loops as they are generally easier to read and faster than typical for loops:
for (Item item: itemList) {
// do some stuff ...
}
All other methods mentioned by other users seems good. Just to expose you to something new, here's my 2 cents. You could use Java Stream API to find any that matches your search term. I find it more readable but it is my personal preference.
class Item {
String name;
public Item(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
// Mock your senario which you have a search term and
// array of object with a public property 'name'
String itemSearch = "test1";
List<Item> itemList = List.of(new Item("test4"), new Item("test2"), new Item("test3"), new Item("test1"));
boolean searchTermExists = itemList
// Create a stream of items from the item list
.stream()
// Searching if any matches the condition (Predicate) and
// return as soon as we find a match
.anyMatch((item) -> item.name.equalsIgnoreCase(itemSearch));
if(searchTermExists) {
System.out.println("\t\t[ITEM FOUND]");
}else {
System.out.println("\t\t[ITEM NOT FOUND]");
}
}
}
And if you want to get the actual first item, then you could use
Item foundItem = itemList
.stream()
.filter((item) -> item.name.equalsIgnoreCase(itemSearch))
.findFirst()
.orElse(null);
System.out.println(foundItem);
there are many ways to search for an item
so after adding the items to the list
use a string to compare with the original item
so if the item is not found a statement will be printed after the loop ends
System.out.println("Enter an item to search for:");
String item = sc.nextLine();
String notFound = null;
here is the code i used to search for a "String" in a list using the "matches" method
System.out.println("Enter an item to search for:");
String item = sc.nextLine();
String notFound = null;
for (int i = 0; i < list.size(); i++) {
boolean check = list.get(i).matches(item);
if(check){
System.out.println("item is found.");
notFound=item;
break;
}
}
if(notFound == null){
System.out.println("item not found.");
}

Iterating through an ArrayList twice when searching for Objects with specified properties

I am trying to iterate through a List, and create a Pair of 2 objects, that are both stored in data. Both objects will have a property isSuspended, one will have the property set to true and other one to false.
This is the code I am using.
Pair<Market, Market> marketPair;
for (Market market : data) {
if (!market.getIsSuspended() && !market.wasProcessed) {
for (Market market2 : data) {
if (market2.getIsSuspended() && !market2.wasProcessed) {
marketPair = new Pair<>(market, market2);
market.setWasProcessed(true);
market2.setWasProcessed(true);
}
}
}
}
However, this code is going to be slow. It is executed on batches with size of 5000. Total number of records is more than 10 millions, so I am searching for a way to make this faster. Do I really need 2 for loops?
Iterate the list once, filtering out wasProcessed. Add entry to one of two lists: isSuspended and notSuspended. Now pair up entries from each list.
List<Market> isSuspended = new ArrayList<>();
List<Market> notSuspended = new ArrayList<>();
for (Market market : data) {
if (! market.wasProcessed) {
if (market.getIsSuspended())
isSuspended.add(market);
else
notSuspended.add(market);
}
}
Iterator iter1 = notSuspended.iterator();
Iterator iter2 = isSuspended.iterator();
while (iter.hasNext() && iter2.hasNext()) {
Market market1 = iter1.next();
Market market2 = iter2.next();
Pair<Market, Market> marketPair = new Pair<>(market1, market2);
market1.setWasProcessed(true);
market2.setWasProcessed(true);
// use marketPair here
}
However, if this is a continual process, processed in batches of 5000, where there might be rollover entries from one of the lists, use a Deque instead.
Deque<Market> isSuspended = new ArrayDeque<>();
Deque<Market> notSuspended = new ArrayDeque<>();
for (;;) { // loop forever
data = /*get next batch here*/;
if (data.isEmpty())
break;
// Find unprocessed
for (Market market : data) {
if (! market.wasProcessed) {
if (market.getIsSuspended())
isSuspended.add(market);
else
notSuspended.add(market);
}
}
// Pair up
while (! notSuspended.isEmpty() && ! isSuspended.isEmpty()) {
Market market1 = notSuspended.remove();
Market market2 = isSuspended.remove();
Pair<Market, Market> marketPair = new Pair<>(market1, market2);
market1.setWasProcessed(true);
market2.setWasProcessed(true);
// use marketPair here
}
}

What is the best approach to split an Arraylist based on their values

I would like to split an ArrayList that I am looping trough and set a field called active which can be true or false. But at the end of loop I would like to split this collection in two groups.. active = false and active = true, so doing this I won't need to search in database twice..
for example:
private List<Classes> searchClasses(ClassItems listItems) {
List<ClassItem> items = new ArrayList<ClassItem>();
for (Iterator<ClassItem> iterator = listItems.getItems().iterator(); iterator.hasNext();) {
ClassItems item = iterator.next();
ClassEntityManager classEnt = ClassEntityManager.search(item.getId);
if(classEnt.active()){
item.setActive(true);
items.add(item);
}
}
return items;
}
What is the best approach to do this??
Make two lists instead of one.
if(classEnt.active()) {
activeItems.add(item);
item.setActive(true);
} else {
inactiveItems.add(item);
}
Use two collections, one for actives and the other for not actives.
When you fetch the data from the DB, simply put the CalssItem in the correct list:
private List<ClassItem> searchClasses(ClassItems listItems) {
List<ClassItem> activeItems= new ArrayList<ClassItem>();
List<ClassItem> notActiveItems= new ArrayList<ClassItem>();
Iterator<ClassItem> i = listItems.getItems().iterator();
while(i.hasNext()) { //This is a better approach.
ClassEntityManager classEnt = ClassEntityManager.search(i.next().getId);
if(classEnt.active()){
item.setActive(true);
activeItems.add(item);
}else{
item.setActive(false);
notActiveItems.add(item);
}
}
List<ClassItem> ret = new ArrayList<ClassItem>(activeItems);
ret.addAll(notActiveItems);
return ret;
}
BUT, in this way, both activeItems and notActiveItems are unreacheable. Best thing to do is to have a loop outside your method that checks if the ClassItem is active or not. In this way both activeItems and notActiveItems can be deleted from the method:
private List<ClassItem> searchClasses(ClassItems listItems) {
List<ClassItem> items= new ArrayList<ClassItem>();
Iterator<ClassItem> i = listItems.getItems().iterator();
while(i.hasNext()) { //This is a better approach.
ClassEntityManager classEnt = ClassEntityManager.search(i.next().getId);
item.setActive(classEnt.active());
items.add(item);
}
return items;
}
And to use the list:
List<ClassItem> items = searchClasses(classItems);
for(ClassItem item: items){
if(item.isActive()){
//do something
}else{
//do something else
}
}
Better yet is to use the magnificient and beautiful Java 8 Stream API:
List<ClassItem> active = items.stream().filter(x->x.isActive).collect(Collectors.toList());
List<ClassItem> notActive = items.stream().filter(x->!x.isActive).collect(Collectors.toList());
or the one liner:
List<ClassItem> active = searchClasses(classItems).stream().filter(x->x.isActive).collect(Collectors.toList());
NOTES:
Your code has a return type of List<Classes>, while the returned value is of List<ClassItem>. Which is right?
Your iterator has a generic type of ClassItem while the next() method returns a ClassItems object. Which is right?

Remove an Object and add another in TreeSet

I am working with Tree Set in java to add some objects in the set . Each object is a signal with different properties. Everytime a new signal comes I set some attributes of the object (Signal) and add it to the set, A signal can come more then one time. For signals I mantain a list in which I store the signal name and when I signal comes I check if the signal is in the list or not If it is not in the list I simply add a new object to my set but if the signal is in the list I take that signal from the set and modify some of its attributes , remove the signal and and add a new one.
The problem is that when I signal comes for the 2nd time or 3rd time and so on it does not remove the old signal and keep all the occurance of the same signal with modified attributes.
Here is my code
SortedSet<Signal> set = new TreeSet<Signal>();
ArrayList< String > messages = new ArrayList< String >();
//If the Message is new one
if(!messages.contains(messageName)){
//System.out.println("I am new signal");
//Add the new Message in Message List
messages.add(messageName);
//Create Object of Message to be stored in Set
signal = new Signal();
signal.setSource(messageSource);
signal.setName(messageName);
if(messageSource.equals("egdci") || messageSource.equals("ugdci"))
signal.setComponent(egdci_ugdci_msgComponent);
else
signal.setComponent(messageComponent);
signal.setOccurance(messageCounter);
signal.setSize(messageContent.length);
signal.setBandwidth(0F);
//Add the new Message Object in the Set
set.add(signal);
}
//If Message already exists in the list
else{
//System.out.println("I am old one");
Iterator<Signal> iterator = set.iterator();
while(iterator.hasNext()) {
signal = (Signal)iterator.next();
if(signal.getName().equalsIgnoreCase(messageName)){
System.out.println("I am here");
int occurance = signal.getOccurance() + 1;
int size = occurance * messageContent.length;
float bandwidth = 0F;
set.remove(signal);
signal = new Signal();
signal.setSource(messageSource);
signal.setName(messageName);
if(messageSource.equals("egdci") || messageSource.equals("ugdci"))
signal.setComponent(egdci_ugdci_msgComponent);
else
signal.setComponent(messageComponent);
signal.setOccurance(occurance);
signal.setSize(size);
signal.setBandwidth(bandwidth);
//Add the new Message Object in the Set
set.add(signal);
}
}
}
writeToCSV(signal , writer);
More over I try to sort my signals in the file on the basis of occurance of a signal, the higher the value of occurance, on the top it should be in the file.
Here is my code of compareTo in Signal.java (I am using Tree Set for the first time so Not sure how to implement my comapreTo)
#Override
public int compareTo(Signal signal) {
int thisOccurance = this.getOccurance();
return thisOccurance.compareTo(signal.getOccurance());
}
Any Help will be highly appreciated...
Thanks in advance
If the problem is only remove the old signal, you can use remove it with:
iterator.remove();
Once the while ends you can create and then add your new Signal to the set.
You else condition should look like:
else {
// System.out.println("I am old one");
final Iterator<Signal> iterator = set.iterator();
boolean deleted = false;
while (iterator.hasNext()) {
signal = (Signal)iterator.next();
if(signal.getName().equalsIgnoreCase(messageName)){
iterator.remove();
deleted = true;
break;
}
}
if(delted){
int occurance = signal.getOccurance() + 1;
int size = occurance * messageContent.length;
float bandwidth = 0F;
signal = new Signal();
signal.setSource(messageSource);
signal.setName(messageName);
if(messageSource.equals("egdci") || messageSource.equals("ugdci"))
signal.setComponent(egdci_ugdci_msgComponent);
else
signal.setComponent(messageComponent);
signal.setOccurance(occurance);
signal.setSize(size);
signal.setBandwidth(bandwidth);
//Add the new Message Object in the Set
set.add(signal);
}
}

Categories

Resources