Infinite recursion in toString(), how can I prevent this? - java

I've searched through StackOverflow trying to figure this out "on my own", with no luck. I think I'm sitting on a problem of infinite recursion in my toString()-method, but I'm not certain as I'm quite new to this.
I think providing you with my code, first of all, will make the problem appear clearer to you:
import java.util.ArrayList;
public class User{
private String name;
private ArrayList<User> friends;
private static ArrayList<User> users = new ArrayList<User>();
public User(String name){
this.name = name;
this.friends = new ArrayList<User>();
users.add(this);
}
public void addFriend(User friend){
friends.add(friend);
}
public static void connect(User user1, User user2){
user1.addFriend(user2);
user2.addFriend(user1);
}
public static ArrayList<User> getUsers(){
return users;
}
public String toString(){
return String.format("%s : %s", name, friends);
}
}
I'm not sure if you can see what I'm trying to do, but the format of my toString is supposed to be like this:
name : {friend1, friend2, friend3, ...}
For example, a user “Alice” with friends “Bob” and “Charlie” would print like this
Alice : {Bob, Charlie, }
I'm not quite sure how I'm supposed to proceed to achieve this. Any help would be much appreciated. I also apologize if this has been answered before, but I didn't understand any of the answers I found earlier.
This is my main-method, might also be helpful:
public class Main{
public static void main(String args[]){
User bob = new User("Bob");
User alice = new User("Alice");
User charlie = new User("Charlie");
User.connect(alice, bob);
User.connect(alice, charlie);
System.out.println(User.getUsers());
}
}

You need to iterate over the "friends" List and just print the name of each. Because you're printing each friend, that includes the friends' friends which is why you're getting infinite recursion.
Try something like
public String toString() {
int count = 0;
StringBuilder sb = new StringBuilder("{\"name\"=" + name + ", \"friends\"=[");
for (User friend : friends) {
sb.append(friend.getName());
if (++count < friends.size()) sb.append(", ");
}
sb.append("]}");
return sb.toString();
}

I suspect that the issue is that when you call Alice.toString(), and you start iterating over the list of Alice's friends, it calls each friend's toString().
If Alice is friends with Bob, and Bob is ALSO friends with Alice, then you end up bouncing back and forth between Alice.toString() and Bob.toString() indefinitely.

From my pont of view, the problem could be that once you call to the toString, trying to display all the users, what happends is that in the toString method you call to the toString of the friends os the user, successively.
In order to avoid recursivity, try to iterate over the list of friends of the user and concat an empty string with the information of the friends of the user, not declaring again the friends of the friends. To sum up, avoid recursivity :)

Related

HashMap with multiple values get.key() error

import java.util.*;
class Student{
final String name;
final String gender;
public String number;
static Map<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
static ArrayList<String> nameandNumber = new ArrayList<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender,nameandNumber);
}
void getPersonByGender() {
String[] Liste = hm.get("Man").toArray(new String[0]);
for (int i = 0; i < Liste.length - 1; i += 2) {
System.out.println(Liste[i] + "\t<------>\t" + Liste[i + 1]);
}
}
}
hello guys i am creating a class and this class will return me 10 student information which I will give (according to the difference between men and women). When i try to use getPersonByGender's function this function gives me all students.
static ArrayList<String> nameandNumber = new ArrayList<>();
new is useful: Count the amount of times your code ever invokes new ArrayList. It's a fairly easy answer: Once. For your entire program.
If you only call new once, that means there is only one list. In the whole system.
No wonder then: This:
nameandNumber.add(this.number);
is called 10 times (because 10 students) for a single 'run' of your app. Thus, that one list you have must therefore have all these numbers added together - that's why you see all the data.
Your code is, unfortunately, layers of bad design decisions.
You can 'fix' the problem (but it'll still be fragile code that is hard to read), or you can 'fix' the design (which is more work).
Fix the problem
Instead of 1 list shared by all students which obviously can't work, you want to call new ArrayList for each student. Get rid of that static single arraylist, and instead make one every time:
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
var nameandNumber = new ArrayList<String>();
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender, nameandNumber);
}
Now you call new ArrayList the right number of times throughout one run of your program, for example.
But you're still in trouble here - because you decided to use a List to represent a single idea (a student), you have confused yourself: A given gender maps to multiple students. Given that a single student is represented by a List<String>, multiple students would be a List<List<String>> and, oof, this is getting real complex, real fast.
We could plug away at fixing this further but let's take a step back and fix your design instead!
Fix the design
More generally, java's typing system is highly nominal: Types have names, and the more descriptive the name, the better.
Student is a far better, clearer name than List<String>. How is a reader of your code supposed to know that those List<String> objects specifically are intended to contain precisely 2 strings, the first of which is the student's name, the second of which is their student ID number? It doesn't say that anywhere. If you mess it up you get no compiler errors of any kind.
You have a type, right there, that properly describes that concept: Student!
So why not replace this bad code:
static Map<String, ArrayList<List<String>>> hm = new HashMap<String, ArrayList<List<String>>>();
With this much improved code:
static Map<String, List<Student>> genderMap = new HashMap<>();
It has all sorts of improvements:
It has a proper name. hm doesn't mean anything.
It uses <> to be shorter - you don't need to repeat that stuff.
It codes to the principle (List) instead of the concrete class.
It uses nominal types - this maps a gender string to a list of students. And the code reads the same way, that's good.
Putting it together:
class Student {
final String name;
final String gender;
final String number;
static Map<String, List<Student>> genderMap = new HashMap<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
List<Student> studentsForThisGender = genderMap.get(gender);
if (studentsForThisGender == null) {
// There is no list yet; we need to make one.
genderMap.put(gender, studentsForThisGender = new ArrayList<>());
}
studentsForThisGender.add(this);
}
static void getPersonByGender() {
Student[] liste = genderMap.get("Man").toArray(new Student[0]);
for (Student student : liste) {
System.out.println(student.name + "\t<------>\t" + student.number);
}
}
}
Note:
getPersonByGender is now static - that's a thing you do to the concept of 'students' not any particula student.
Instead of this nebulous 'print list[0] - you just sorta have to know that's the name', we print student.name which documents itself.
We fixed the problem where you were list-confused.
If you get a little further along your java studies, that 'get the list of students for a given gender, and make a new list if neccessary' can be put more succintly. The last 4 lines can be reduced to:
genderMap.computeIfAbsent(gender, () -> new ArrayList<>()).add(this);
But I bet that syntax, and what is happening there, hasn't been covered yet in your java course.

Problem with comparing a value to a specific object of an ArrayList Java

I have the class Student with a constructor that sets the values int s_code, String name and int age.
When I create an object of the class Student I pass it into an ArrayList AllStudents.
My problem is that I want the user to enter an Id and check if there is that Id in the ArrayList. If its not let him add a new student else tell him to try again.
I tried to loop through the ArrayList with for and inside of it
I have an if statement with .contains and if it is true I have a simple println("Good") just to test it.
When I run my program though it skips it.
Here is my code:
static ArrayList<Student> AllStudents = new ArrayList<Student>();
static void InitStudents() //this is a method that creates some students when I call it in main.
{
AllStudents.add(new Student(1,"James",15));
AllStudents.add(new Student(2,"John",16));
AllStudents.add(new Student(3,"Rose",15));
}
System.out.println("Enter the ID of the student you want to add.");
Scanner get_new_code = new Scanner(System.in);
int s_code = get_new_code.nextInt();
for(Student code : AllStudents)
{
if (AllStudents.contains(s_code)) //I think that I have to include age and name for it to work.
{
System.out.println("Good");
}
}
By the way sorry if I didn't explain something or I did something completely wrong I'm new to Java.
That advanced loop is not helping you in the way you implemented it.
for(Student code : AllStudents){ //"code" is one element out of the list
if (AllStudents.contains(s_code)){ //here you are checking the whole list
System.out.println("Good");
}
}
This might be what you are looking for:
for(Student code : AllStudents){
if(code.getSCode() == s_code){ //here the one element named "code",
//out of the list, will be checked
System.out.println("Good");
}
}
A getter method (this one is called for example getSCode()) will help you here, to ask for every attribute of a student object. It will return the s_code of the object you are looking at.
EDIT AS EXAMPLE:
public class Student{
int s_code;
String name;
int age;
public Student(int code, String name, int age){
this.s_code = code;
this.name = name;
this.age = age;
}
public int getSCode(){
return s_code;
}
public int setSCode(int newSCode){
this.s_code = newSCode;
}
}
With the getter and setter you can ask for data of an object or you can set the data.
AllStudents contains students and s_code is an int.
You can search by id mapping it first. Assuming the code is in a field called Id.
allStudents.stream().map(s -> Student::getId).collect(Collectors.toList()).contains(s_code);

How to test my java program?

I was asked to write this part of a program and then test it in the following way:
Add some statements to the main method that create two or three contact instances, add them to the address book, and then search for one or two of them. Display the result of each search to see if they were retrieved correctly. Then remove one of them and show the list to be sure that it was removed correctly.
I wasn't entirely sure how to test this. What should I write in the main class? Also, is the rest of the code correct?
Do this for Contact Class:
Create this class in the client package. This class should have three fields (An email address, A full name, A nick name). All of these should be Strings.
Provide two constructors: one full constructor (parameters for all three member variables), and one that has a parameter only for the email address. This second constructor will leave the other two fields null.
Add a toString method (and use the #Override annotation) that works like this:
Contact c1 = new Contact("jenny#gmail.com");
c2.toString() should return "jenny#gmail.com".
Contact c2 = new Contact("jenny#gmail.com", "Jennifer Abott", "jenny");
c2.toString() should return "Jennifer Abott (jenny) jenny#gmail.com".
Do this for AddressBook Class:
Create this class and have a single member variable of type ArrayList<Contact>. Define the variable, don't just declare it
You do not need a constructor.
Write an add method that has a Contact parameter and adds the contact to the contact list.
Write a remove method that has a String parameter, which is the nick name of the contact to remove. It returns nothing. Remove that contact from the contact list. Hint: Use the search method that you already wrote in order to find the contact, then remove that contact from the contact list. See the online documentation for ArrayList for how the remove method works in the ArrayList class. Be sure that the remove method does not crash if you give it a nick name that does not exist in the list.
Write a search method that has a String parameter, which is a nick name to search for. The method must iterate over the contact list. If the nick name is found (use .equals), return that contact. If no contact is found, return null.
Write a show method that displays each Contact instance. It has no parameters and returns nothing. Display one contact per line, and number each line. Like this:
Jeff Meunier (jeff)
Bill Gates (money)
Vladimir Putin (vman)
Make sure that the numbers shown start at 1, not at 0.
Contact class:
package client;
public class Contact
{
private String _emailAddress;
private String _fullName;
private String _nickName;
public Contact(String emailAddress, String fullName, String nickName)
{
_emailAddress = emailAddress;
_fullName = fullName;
_nickName = nickName;
}
public Contact(String emailAddress)
{
_emailAddress = emailAddress;
}
#Override
public String toString()
{
if(_fullName == null)
{
return "<" + _emailAddress + ">";
}
else
{
return _fullName + " " + "(" + _nickName + ")" + " " + "<" + _emailAddress + ">";
}
}
}
Address Book class:
package client;
import java.util.ArrayList;
public class AddressBook
{
public ArrayList<Contact> contactList = new ArrayList<Contact>();
public void add(Contact _contact)
{
contactList.add(_contact);
}
public Contact search(String nickName)
{
for (int n=0; n < contactList.size(); n++)
{
if(contactList.get(n).equals(nickName))
{
return contactList.get(n);
}
}
return null;
}
public void remove(String nickName)
{
if(search(nickName) != null)
{
contactList.remove(search(nickName));
}
}
public void show()
{
for(int n=0; n<contactList.size(); n++)
{
System.out.println(n++ + ". " + contactList.get(n).toString());
}
}
}
I don't have much in the main class yet, but here it is:
Main Class
import java.util.ArrayList;
import client.Contact;
public class Main
{
public static void main(String[] args)
{
Contact c1 = new Contact("jeffm#engr.uconn.edu");
Contact c2 = new Contact("jeffm#engr.uconn.edu", "Jeff Meunier", "jeff");
}
}
"Add some statements to the main method that create two or three contact instances, add them to the address book, and then search for one or two of them. Display the result of each search to see if they were retrieved correctly. Then remove one of them and show the list to be sure that it was removed correctly."
If this is your task then:
1. You created a few contact instances, so this is done, but create a few more with all the available class fields set (this will be needed for the next steps)
2. Now you must create an instance of your address book class.
AddressBook addressBook = new AddressBook();
Add the created contacts to the adress book
addressBook.add(c1);
addressBook.add(c2);
// other contacts
Use the show method to see if everything is correct.
addressBook.show();
Try removing one and show the available contacts again to see if this worked.
addressBook.remove(c1.getNickName()); // Make get method for the nickname
addressBook.show();
Then search for a contact with a certain name, assign this to a new contact object and then print something from it to acknowledge that the object is the correct one.
Here your logic can break, because your search does not cover the possibillity that you have more then one contacts with the same name. So you should return an List of found contacts instead of one contact. Implement this and search again (make sure you search for objects with unique and non-unique names so the testing is acceptable).
Your search testing will look something like this at the end:
List <Contact> contacts = addressBook.search("John");
for(Contact contact in contacts) {
System.out.println(contact.toString());
}
Now since your delete method depends on the search, you will have to change it a bit too. When you do that, test the remove method again as above.
Work out every step until it works fine and your done.
P.S. You may want to print some description between different testing prints, so you can see clearly the results in the output

Can I put a String in an Arraylist when a object is created?

I want to make a program where you can name a String("weapon" for example)and then add that String to a ArrayList. But without typing it yourself like:
MyArrayList.add(Egg); //for example
So that the new Object automatically add to the Arraylist.
So what I did, I created an Arraylist that will hold the weapon names. I made a method, where you can "make" in the main class a object with the weapon name.But how do i make something that when a object (in the main class)is created, it automatically add it self to the arraylist.
(Sorry for bad explaining, I'm from The Netherlands so... If it's a bad explaining please tell me so i can improve it)
Maybe I completely misunderstand it, but do you want to do something like this?
private ArrayList<YourObject> list;
public YourMainClass(){
list = new ArrayList<YourObject>();
}
public void onAdd(String weaponName){
list.add(new YourObject("weaponName")); // <- this being your issue
}
With YourObject being something like:
public class YourObject
{
private String name;
public YourObject(String n){
setName(n);
}
public void setName(String n){
// Perhaps an if-else to check if n isn't null, nor emtpy?
name = n;
}
public String getName(){
return name;
}
}
Based on my interpretation of your problem, you want to have the user create a weapon name and add it to the ArrayList without having to manually add the code to add it?
A basic way to get String input from a user:
Scanner inputscan = new Scanner(System.in); //reads input from command line
String weapon = inputscan.nextLine(); //waits for user input
MyList.add(weapon);
That way, every time you call the "make" method with that code in it, it will prompt the user to type a weapon name, then the weapon name gets stored in the array.
I think you want to initialize the List with an object in it:
Try using an instance block, like this:
List<String> list = new ArrayList<String>() {{
add("Egg");
}};
Add the command to add the object to the collection in the constructor.( But this ill advise)
You can create an auxiliary class that will create that object and label to the collection.
class WeaponFactory
{
Collection c;
public WeaponFactory(Collection coll){c=coll;}
public Weapon makeWeapon(String text) // Makes sense when the object is not String , like a Weapon class which also contains stats or something
{
Weapon w = new Weapon(text)
c.add(w);
return w;
}
}
class Weapon
{
String name;
public Weapon(String text)
{
name = text;
}
}

Creating new classes on code execution

Trying to make code to work with this main:
// TestClass class ResselerTest {
public int ValueOfContainer(Object conatiner, carmodel) { //have to count value of Mazda cars
}
public static void main(String[] args) {
Reseller Joe = new Reseller();
//name cash in k's
Customer John = new Customer("John", 200);
John.get(new Ford(5)); // (i) no of cars. John want to buy 5 ford's
John.get(new Ferrari(5));
John.get(new Mazda(3));
ShoppingCart JohnCart = John.getShoppingCart();
System.out.println("Before payment\n" + JohnCart);
// Hes paying right now!
John.pay();
System.out.println("Paid\n" + John.getShoppingCart());
System.out.println("John got : " + John.getCash() + " USD");
// Now we need to pack them into container
Container JohnContainer = new Container(John);
janek.pack(JohnContainer);
// After packing to conainer.
System.out.println("After packing to conainer\n" + John.getShoppingCart());
// Check whats in container
System.out.println(JohnContainer);
// Lets see how much John paid for white cars.
System.out.println("White cars cost: " +
valueOf(JohnContainer, "white") );
} }
// ---------------------------------------------------------------
class Reseller {
public Reseller() {
// PriceList Singleton!!!
PriceList pl = PriceList.getInstance();
pl.set("Ford", 24);
pl.set("Ferrari", 120);
pl.set("Mazda", 9); //price in k's }
}
//---------------------------------------------------------------
public class Customer { String name; int cash;
public Customer(String name, int cash) {this.name = name; this.cash = cash;
}
public }
//---------------------------------------------------------------
public class ShoppingCart( { //public ShoppingCart // no idea
}
//---------------------------------------------------------------
public class PriceList { public static PriceList instance = null; HashMap<String, Integer> prices = new HashMap<String, Integer>();
public static PriceList getInstance() { if (instance == null) { instance = new PriceList(); } return instance; }
public void set(String car, int value){
prices.put(car, value);
}
// singleton
public PriceList() {
}
}
My main problem.
How to make John.get(new Mazda(3)); to work <sic!>
And how to link cars to color. As far was sad that 1 car have 1 color (Ferrari => ofc. Red :))
I will appreciate any help from you fellows.
It appears that get() doesn't work because you haven't written this bit yet. I suggest you try to write this method (at least the declaration)
It appears you need a structure/class which associates Car and Color.
I suggest you use camelCase for variable names.
I think there is something rather wrong with your object model.
It appears that you have a class for each model of Car. I'd have thought that where needed to be a single Car class, which had attributes such as "model", "color", "price" and so on.
It appears that an instance of (say) Ferrari is supposed to represent a number of cars, rather than a single Car.
I'm not going to tell you exactly how to implement this, since it looks like homework to me.
Well, first of all get Object Oriented concepts clear. What is a person to car relationship.
Secondly, John.get(new Mazda(3)) is not a very good program. The method names should indicate what they do. Here get method is used for actually setting the purchase made by that customer. John buys 3 Mazdas when we call John.get(new Mazda(3)) Is that understanding correct?
The advantage of OOP is that we can map real world entities and talk in real world terminologies in our program. So something like makePurchase(..), submitOrder(..), checkOut(..) would make sense.
It is often said that the programming language helps us think. (sometimes they limit our thoughts :))
Also, a good OO design will say something like a Customer class has a method:
checkOut(Set cars) instead of
checkOut(Honda[] hondaCars)
Does this help?

Categories