Delete an object from ArrayList by iterator - java

I want to create a program which would be like a home budget, so I have a class AmountModel
(I know that Integer is not so good for id, but it's not a problem now):
import java.time.LocalDate;
public class AmountModel {
private Integer id;
private Double amount;
private CategoryModel categoryModel;
private LocalDate localDate;
// getters/setters etc.
}
And in another class I built this deleteAmount method:
static Scanner sc = new Scanner(System.in);
public List<amountModel> deleteAmount() {
Iterator<AmountModel> it = amountList.iterator();
while (it.hasNext()) {
System.out.println("Choose index to delete ");
AmountModel am = it.next();
if (am.getId().equals(sc.nextInt())) {
it.remove();
}
break;
}
return amountList;
}
Adding object works good, but when I try to use the delete method I have to put first index.
Example:
I have three objects (with index 0, 1, 2).
When I choose 1 or 2 program do nothing.
When I choose 0 program deletes first index, remains index 1 and 2.
When I choose 2, program do nothing.
When I choose 1, program deletes index 1, remains index 2... etc.
What is wrong with this method?

You should separate your input logic from your delete logic and accept the list as a parameter.
Note: this only works with a mutable list. If you use something like Arrays.asList() it will throw an exception.
public void deleteAmount(List<AmountModel> list, int key) {
list.removeIf(a -> a.getId().equals(key));
}

Welcome to Stack Overflow!
As others have mentioned, there are a few ways to tackle this. But I think you can make this even simpler by changing the data structure used to access your AmountModel collection: if you're frequently accessing an item by ID, a Map is a great fit.
No more worrying about iterator state; you could just do something like:
// Map "amounts" by ID for easy O(1) lookup.
static Map<Integer, AmountModel> amountMap
public void deleteAmount(Integer id) {
if (!amountMap.containsKey(id)) {
// (TODO: Handle invalid input)
throw new Exception()
}
amountMap.remove(id)
return
}
Hope this helps! I threw together a working example in a gist here if you're interested. (In Groovy, but should be enough to give you the idea)

Your break statement is breaking the while loop in the first iteration only. So, it will work only if the first am.getId() matches with your fist input.
Also, your sc.nextInt() will keep on scanning for next available input, Remove it from while loop.
static Scanner sc = new Scanner(System.in);
public List<AmoutModel> deleteAmount() {
Iterator<AmoutModel> it = amountList.iterator();
Integer scId = sc.nextInt();
while (it.hasNext()) {
System.out.println("Choose index to delete ");
AmoutModel am = it.next();
if (am.getId().equals(scId)) {
it.remove();
break;
}
}
return amountList;
}

call your sc.nextInt() outside of the loop, otherwise it will get run everytime the loop returns, as the condition gets reevaluated every time the loop ends.
also you could use the remove method of list
static Scanner sc = new Scanner(System.in);
public List<AmoutModel> deleteAmount() {
System.out.println("Choose index to delete ");
int index = sc.nextInt();
amountList.remove(index);
return amountList;
}

Related

Java: 2 Classes, One Main(Tester) and other class(methods). Getting input from tester and passing to other class's ArrayList

New to this website and coding in java in general. I have about 5 months experience from last year's classes. and another half spent in python. We have reached our second lab this semester and I have ran into a slight problem that my new professor refuses to help / guide with.
The lab is as described.
Make a to do list that has 2 classes. One class (the main tester one) takes in all the IO and gives all the output. The other class contains the todo list itself (arraylist) and provides methods for the main class to call on. The scanner object is in the main tester class. the arraylist is in the other class.
The following commands of input are necessary.
List, New, Change, Delete, Quit.
The program basically prompts the message to add something to your to do list. add whatever the whole line is. Then modify it as you do them.
My current questions regards how to take the input from the tester program and pass it to the other class to store into the arraylist. We have done labs where they were all in the main class and I would just add with the method calls of the Arraylist class. however now we must do it this way and I am having troubles getting them to communicate. Im guessing that I just have some sort of constructor wrong or some object misreferenced. Here is a short snippet of what I have.
(Main Tester Class)
import java.util.Scanner;
public class ToDoTester
{
public static void main(String[] args)
{
Scanner in = new Scanner (System.in);
ToDo info = new ToDo();
while(true)
{
System.out.println("* * * Start To Do List * * *");
System.out.println("Enter a command (List, New, Change, Done) or Quit
");
String x = in.next();
if(x.equals("List"))
{
System.out.println(info.getList());
//Print out the array list
}
else if(x.equals("New"))
{
//System.out.println(info.addList(addedListOption));
//Add new thing to do to arraylist
}
(Other Class With Methods)
public class ToDo {
ArrayList<String> data = new ArrayLst<String>();
ToDoTester scan = new ToDoTester();
private String list;
public void addList() {
//tring takeIn = ToDoTester.scan.next();
//data.add(scan.nextLine());
}
public String getList() {
System.out.println(data.size());
return list;
}
}
I realize this may be a very nooby problem and perhaps i'm making more of a deal out of it than I have to. But i truly want to understand what I am doing and this new professor as compared to my old one is slightly worse in the helping understanding department. Thus, i come here for help, not for someone doing my work! So any help would be appreciated and I can supply any other information as needed. Can even post a picture of the lab if necessary or easier.
Original answer
You are doing well, but you probably forgot that we can pass in objects to methods via method arguments.
class ToDo {
public void addToList(String newToDo) {
data.add(newToDo);
}
}
class ToDoTester {
public static void main(String[] args) {
...
} else if (x.equals("New")) {
info.addToList(in.nextLine());
}
}
}
Expanded answer
Here is a simplified version of how I would perform the task.
As I mentioned in the comments, there is room for improvement: we can replace the switch with the command pattern, we can separate the entry point from our domain classes, we can handle exceptions more accurately, we can customise the output messages more elaborately.
Anyway, first take a look at this, and then let me know if you want to see a more enhanced version.
final class ToDoTester {
private final Scanner reader = new Scanner(System.in);
public static void main(String[] args) {
final ToDo toDo = new ToDo();
final ToDoTester toDoTester = new ToDoTester();
final Scanner reader = toDoTester.getReader();
while (true) {
System.out.println("Enter a command (List, New, Change, Delete) or Quit:");
switch (reader.nextLine()) {
case "List":
toDoTester.display(toDo.list());
break;
case "New":
toDo.add(toDoTester.readToDo());
break;
case "Change":
final Pair<Integer, String> input = toDoTester.readInputToChange();
toDo.update(input.getKey(), input.getValue());
break;
case "Delete":
toDo.remove(toDoTester.readIndex());
break;
case "Quit":
return;
default:
System.out.println("Incorrect choice, let's try again.");
}
}
}
public Scanner getReader() {
return reader;
}
public void display(List<String> list) {
IntStream.range(0, list.size())
.mapToObj(i -> String.format("[%d] %s", i, list.get(i)))
.forEach(System.out::println);
}
public Pair<Integer, String> readInputToChange() {
return new Pair<>(readIndex(), readToDo());
}
public String readToDo() {
System.out.println("Enter a to-do:");
return reader.nextLine();
}
public int readIndex() {
System.out.println("Enter the index of the to-do:");
return Integer.valueOf(reader.nextLine());
}
}
final class ToDo {
private final List<String> list = new ArrayList<>();
public void add(String toDo) {
list.add(toDo);
}
public void remove(int position) {
validatePosition(position);
list.remove(position);
}
public void update(int position, String substitute) {
validatePosition(position);
list.set(position, substitute);
}
private void validatePosition(int position) {
if (position < 0 || position >= list.size()) {
// should be thrown an exception
System.out.println("Incorrect input.");
}
}
public List<String> list() {
return new ArrayList<>(list);
}
}
An example of execution:
Enter a command (List, New, Change, Delete) or Quit:
List
Enter a command (List, New, Change, Delete) or Quit:
New
Enter a to-do:
first to-do
Enter a command (List, New, Change, Delete) or Quit:
List
[0] first to-do
Enter a command (List, New, Change, Delete) or Quit:
Change
Enter the index of the to-do:
0
Enter a to-do:
my first to-do
Enter a command (List, New, Change, Delete) or Quit:
List
[0] my first to-do
Enter a command (List, New, Change, Delete) or Quit:
Delete
Enter the index of the to-do:
0
Enter a command (List, New, Change, Delete) or Quit:
List
Enter a command (List, New, Change, Delete) or Quit:
Your todo class must have arraylist and puplic methods, that are get string (for add method) or int (for delete) or nothing (for list) as arguments, and operate on arraylist. Your ToDoTester class must contain example of todo class (ToDo info = new ToDo();) and operate on its methods in loop. Hope that helps

removing objects from an arrayList and printing out the remain objects

i have a code that stores the number of books in arraylist, every time a book is borrowed the user is prompted to input a number starting from zero which represents the first book and 1 for the second and so on. it runs at first deleting the first object from the arraylist, then when it gets to the last, it throws an IndexOutOfBoundException, here's my block of code, please help!
//class doesn't do much than just gets the title of the book
public class BookLibrary {
String title;
public BookLibrary(){}
public void setTitle( String names) {
title = names;
}
public String getTitle(){
return title;
}
}
here's my code that deletes the object from the arraylist
public class LibraryAssistant {
ArrayList<BookLibrary> booklib = new ArrayList<>();
int numOfBooks = 0;
public void setupLibrary(){
BookLibrary bc = new BookLibrary();
bc.setTitle("fantastic beasts and where to find them");
BookLibrary bc1 = new BookLibrary();
bc1.setTitle("Harry potter and the prisoner of azkabans");
BookLibrary bc2 = new BookLibrary();
bc2.setTitle("one day for the thief");
booklib.add(bc);
booklib.add(bc1);
booklib.add(bc2);
numOfBooks++;
for(BookLibrary book : booklib){
System.out.println(book.getTitle());
}
}
public void borrowbook(){
while(!booklib.isEmpty()){
String getbooknum = userInput("please enter a book number of the book you want to borrow");
int index = Integer.parseInt(getbooknum);
if(index>=0){
booklib.remove(index);
for(BookLibrary lb : booklib){
System.out.println(lb.getTitle());
}
}
numOfBooks--;
} if(booklib.isEmpty()){
System.out.println("the library is empty");
}
}
The reason behind the IndexOutOfBoundException is the size of arraylist gets decremented everytime you remove the object. so if you remove the first title (index 0) from the book the size of list will be 2. when user go for second removal (index 1), it will work fine. But when user will go for 3rd removal (index 2), it will throw IndexOutOfBoundException as the size of ArrayList is 1 now.
My first observation is that numOfBooks in setUpLibrary() should be set to 3. or should be updated as the size of the array list.
The second observation is that in borrowbook(), while loop runs until the bookLib becomes empty. So line booklib.remove(index); will throw error IndexOutOfBoundsException when there are no more elements or the number given by the user is out of range.
Please refer below documentation:
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#remove(int)

How do I avoid ConcurrentModificationException in ArrayList ONLY when iterating?

For clarification - I DO NOT want to remove anything from the ArrayList. Therefore 90% of all the answers I have found don't actually apply. I can't find anything here, or elsewhere that helps me out much!
I'm writing a Java Application to play Hangman where the opponent (computer) is essentially cheating, in the sense where it does not 'choose' a word, it has a group of words and decides if the player's guess is correct, or incorrect, depending on which of those leaves the more difficult group of words to guess from.
In a nutshell, my problem is this:
I have an ArrayList, masterList, where I have a set of words, a dictionary if you will, and various methods iterate through this to perform various tasks. My code is single threaded and one of these methods is throwing a ConcurrentModificationException when trying to access the next object in the ArrayList in the second iteration. However, I cannot find anything that actually changes the ArrayList during the iteration.
import java.io.*;
import java.util.*;
public class Main {
private ArrayList<String> masterList;
private ArrayList<String> contains;
private ArrayList<String> doesNotContain;
private HashMap<Integer, ArrayList<String>> wordLengthList;
private HashMap<Integer, ArrayList<String>> difficultyList;
private int guesses = 10;
private Scanner sc;
private FileReader fr;
private BufferedReader br;
private String guessString;
private char guessChar;
private static final String DICTIONARY = "smalldictionary.txt";
private String wordLengthString;
private int wordLengthInt = 0;
public Main(){
masterList = new ArrayList<String>();
contains = new ArrayList<String>();
doesNotContain= new ArrayList<String>();
wordLengthList = new HashMap<Integer, ArrayList<String>>();
difficultyList = new HashMap<Integer, ArrayList<String>>();
sc = new Scanner(System.in);
importTestDictionary(); //does not use masterList
br = new BufferedReader(fr);
importWords(); //Adds to masterList. Both readers closed when finished.
catalogLengths(); //Iterates through masterList - does not change it.
do{
setWordLength(); //does not use masterList
}while(!(validateLengthInput(wordLengthString))); //validation will change the set of masterList if valid.
//Main loop of game:
while(guesses > 0){
do{
getUserInput();
}while(!(validateInput(guessString)));
splitFamilies();//will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
printDifficultyList();
}
}
private void importWords(){ //Adds to masterList. Both readers closed when finished.
try{
while(br.readLine() != null){
line = br.readLine();
masterList.add(line);
}
br.close();
fr.close();
}catch(IOException e){
System.err.println("An unexpected IO exception occurred. Check permissions of file!");
}
}
private boolean validateLengthInput(String length){ //validation will change the set of masterList if valid.
try{
wordLengthInt = Integer.parseInt(length);
if(!(wordLengthList.containsKey(wordLengthInt))){
System.out.println("There are no words in the dictionary with this length.\n");
return false;
}
}catch(NumberFormatException e){
System.out.println("You must enter a number.\n");
return false;
}
masterList = wordLengthList.get(wordLengthInt);
return true;
}
private void splitFamilies(){ //will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
Iterator<String> it = masterList.iterator();
int tempCount = 0;
while(it.hasNext()){
tempCount++;
System.out.println("tempCount: " + tempCount);
String i = it.next(); //Still throwing ConcurrentModification Exception
if(i.contains(guessString)){
contains.add(i);
}else{
doesNotContain.add(i);
}
}
if(contains.size() > doesNotContain.size()){
masterList = contains;
correctGuess(); //does not use masterList
profileWords();
}
else if(doesNotContain.size() > contains.size()){
masterList = doesNotContain;
incorrectGuess(); //does not use masterList
}
else{
masterList = doesNotContain;
incorrectGuess(); //does not use masterList
}
}
private void printMasterList(){ //iterates through masterList - does not change it.
for(String i : masterList){
System.out.println(i);
}
}
private void catalogLengths(){ //Iterates through masterList - does not change it.
for(String i : masterList){
if(i.length() != 0){
if(!(wordLengthList.containsKey(i.length()))){
wordLengthList.put(i.length(), new ArrayList<String>());
}
wordLengthList.get(i.length()).add(i);
}
}
}
}
The line the exception is thrown from is marked above in the code. Any method using masterList is also marked, any method included that does not use it, there is no comment against.
I did read some answers and some of them suggested using Iterator to avoid the exception. This is implemented above in splitFamilies(). The original code was as below:
private void splitFamilies(){ //will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
int tempCount = 0;
for(String i : masterList){ //This line throws ConcurrentModificationException
tempCount++;
System.out.println("tempCount: " + tempCount);
if(i.contains(guessString)){
contains.add(i);
}else{
doesNotContain.add(i);
}
}
....continue as before
tempCount is always 2 when the exception is thrown.
Maybe I'm missing something really simple, but I've tried tracing this, and cannot find out why I'm getting this exception!
I've tried to remove everything irrelevant from the code, but if anyone really wants to view the full thing, I guess I could dump all my code in the question!
The issue comes from the fact that masterList is a reference to either contains or doesNotContain after a first split. When you iterate on masterList, you actually also iterate at the same time on that other list.
So, then you add items to the lists:
if(i.contains(guessString)){
contains.add(i);
}else{
doesNotContain.add(i);
}
Here you do not only add items to contains or doesNotContain, but also potentially to masterList, which leads to the conccurentException.
To solve your issue, just make a copy of your lists, instead of : masterList = contains;
do a copy with: masterList = new ArrayList<>(contains);
And the same for doesNotContains.
Another solution which comes to mind is to reset the two lists contains and doesNotContains for each split. Since you only use them in this method, and nowhere else, remove these two lists from your Class, and defines them as private variables inside splitFamilies

Returning the String at the index typed in by the user (ArrayList)

I'm trying to create a simple method. Basically, I want this method (called "returnIndex") to return the word at the ArrayList index number the user types in.
Example:
If the user types in "1", is should return whatever String is at index 1 in the ArrayList.
This is what I have so far:
public void returnIndex ()
{
Scanner in = new Scanner (System.in)
while (in.hasNextLine())
{
if (in.equals(1))
{
//return item at that index
}
}
}
I'm just not sure how to say "return the item at that index" in Java. Of course, I'll have to make the code work with any other number, not just '1'. But for now, I'm focusing on '1'. Not even sure if the in.equals(1) part is even 100% right.
My apologies if this question seems a little elementary. I'm still working on my Java. Just hints please, no complete answers. Thank you very much.
public String returnIndex(Scanner in, List<String> list) {
return list.get(in.nextInt());
}
Don't create new Scanners as it can cause subtle problems. Instead, create only one and keep using it. That means you should pass it into this function.
There's no need to use ArrayList when List will do (as it will here).
You need to make the function return String, not void, if you want it to return a String.
public static void main(String[] args) {
List<String> values = new ArrayList<String>();
values.add("One");
values.add("Two");
values.add("Three");
String result = getStringAtIndex(values);
System.out.println("The result:" + result);
}
public static String getStringAtIndex(List<String> list) {
Scanner scanner = new Scanner(System.in);
int index = 0;
index = scanner.nextInt();
return list.get(index-1);
}

I get a compile time error when accessing an ArrayList with a new method

I am attempting to access an ArrayList that was created in a different method within the same class. The scanner method pulls in data from a text file. The text file data appears this way: 123 12 1 43, with line breaks...
Currently, my method works to pull in the data, but does not compile after that method ends. I originally had the entire code within the same method and it worked fine. But I'd like to return the largest value by creating a new method within this class, and then create a tester class that will access this new method. Here is my existing code. Or if there is a better solution. I'm all ears.
public class DataAnalyzer {
public DataAnalyzer(File data) throws FileNotFoundException
{
List<Integer> rawFileData = new ArrayList<>();
FileReader file = new FileReader("info.txt");
try (Scanner in = new Scanner(file)) {
while(in.hasNext())
{
rawFileData.add(in.nextInt());
}
}
}
public int getLargest(rawFileData){
int largest = rawFileData.get(0);
for (int i = 1; i < rawFileData.size(); i++){
if (rawFileData.get(i) > largest)
{
largest = rawFileData.get(i);
}
}
for (Integer element : rawFileData){
if (element == largest)
{
System.out.print("This is the Largest Value: ");
System.out.print(element);
}
}
}
}
Your main issue is with your method declaration. It needs a type parameter:
public int getLargest(List<Integer> rawFileData)
Note the List<Integer>.
Now, there is already a method for this in the Collections utility class. You would do well to look over that link in detail - there are many useful methods there. To get the highest element from a Collection of Objects that have a natural order (such a Integer). For example
int largest = Collections.max(rawFileData)
So your method can be reduced to:
public int getLargest(List<Integer> rawFileData)
return Collections.max(rawFileData);
}
You need to think over your logic much more carefully before you begin to write code, for example, your first loop is good:
int largest = rawFileData.get(0);
for (int i = 1; i < rawFileData.size(); i++){
if (rawFileData.get(i) > largest)
{
largest = rawFileData.get(i);
}
}
You do exactly what any programmer would do. But then, instead of returning the largest when you find it, you for some reason loop again:
for (Integer element : rawFileData){
if (element == largest)
{
System.out.print("This is the Largest Value: ");
System.out.print(element);
}
}
Ask yourself what does this do? You have a List of, say, apples. You look at each one and compare them - finding the largest apple. You now have the largest apple in the List. You then loop over the List again looking for an apple that matches the apple you have already found. Why do this?
Further, you never return from the method. Your method is declared as returning an int; but you never do.
The missing type in your method definition is the problem here.
Change the method definition from
public int getLargest(rawFileData) {
....
}
to
public void getLargest(List<Integer> rawFileData) {
....
}
And the second for loop in the method is unnecessary. The largest integer is already stored in the variable "largest" and you can print it after the first for loop.

Categories