0

Edit: The rules are that I cannot modify the formula. The methods have to be void and I must pass the preinitialized array to those methods. These are the constraints that will be present for the future assignment itself, which is why I'm trying to play around with this namebag program this way.

I am trying to write a "namebag" program for fun and also for practice for a future assignment. The goal is to let the user have a bag. In this bag, they can store 25 names. They can add 1 name at a time, remove 1 name at a time, sort the bag so that the names are in alphabetical order, etc. Basic array exercises. I'm running into some issues since I'm not very familiar with Java, though. I have a function that takes the array, converts it into a list, attempts to add a name of the user's choice, and then attempts to convert that list BACK to the array that it once was. And then I have another function that attempts to display everything in the array.

This code works when I avoid using a menu, the switch case, and methods. I can store things into the name bag and I can display the names without any problem and without displaying the null values. But I don't understand why the names won't display in the code down below. Is there a way to get a sort of pass-by-reference effect in Java? Is there a better way to do this kind of program? I appreciate any advice!

This is my code:

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Arrays;

public class nameBag {
    public static void main(String[] args) {

        String[] nameBag = new String[25];
        Scanner input = new Scanner(System.in);

        while (true){

            System.out.println("Welcome to the bag of names.");
            System.out.println("1. Add an item to the bag.");
            System.out.println("2. Display items in bag.");
            System.out.println("0. Exit program.");

            int userChoice = input.nextInt();
            switch (userChoice){
                    case 1:
                    addName(nameBag);
                    break;
                case 2:
                    displayNames(nameBag);
                    break;
                case 0:
                    System.out.println("See ya!");
                    System.exit(0);
            }
        }
    }

    public static void addName(String[] nameBag){
        ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(nameBag));
        Scanner input = new Scanner(System.in);
        System.out.println("What name do you want to add to the pack?");
        String userSelection;
        userSelection = input.nextLine();
        nameList.add(userSelection);
        nameBag = nameList.toArray(nameBag);
    }

    public static void displayNames(String[] nameBag){
        for (String s : nameBag) {
            if (!(s == null)) {
                System.out.println(s);
            }
        }
    }
}
HandleThatError
  • 598
  • 7
  • 29
  • Are you sure your code compiles without errors? See: `displayNames(nameBag;` – Daniel Jun 11 '14 at 06:07
  • For an explanation of parameter passing in Java see for example http://stackoverflow.com/questions/1068760/can-i-pass-parameters-by-reference-in-java. In your case you should simply return the new array from the method. – Henry Jun 11 '14 at 06:08
  • You have to return your edited array and assign it to `nameBag` but there is a much worse problem: Your pre-initialized array is useless because you convert it to list, add a new element to list and convert "that" list into a new array, so those 25 elements will always be "null". Just store names in a global arraylist variable and do add/remove processes without returning any list object. – Mehmet Sedat Güngör Jun 11 '14 at 06:11
  • Apologies on the missing parenthesis, Daniel. I edited it and it compiles fine. It lets me add names when I select option 1 but when I try to display the array, I don't see the names I added. Henry, if I am not allowed to return new arrays (due to an artificial constraint of requiring void methods) is there any other way? I'll look into AtomicReference. Never used it before so I have no idea how to implement it. – HandleThatError Jun 11 '14 at 06:13
  • Mehmet, I just edited my first post to mention that there are some artificial constraints being imposed. My professor will want me to use void methods and he will want me to pass the preinitialized array to those methods. But let's say I use a global arraylist variable. If I pass that global arraylist variable to a method that adds items to the list, how can I make sure that the global arraylist variable will have those additions? – HandleThatError Jun 11 '14 at 06:17
  • @HandleThatError for simulating call-by-ref see this answer http://stackoverflow.com/a/1068831/1796579 – Henry Jun 11 '14 at 06:20
  • You cannot pass nameBag as a parameter and add values to those parameters. The values will get lost each time function addName() ends execution. You can declare a static variable nameBag under the class variables and manipulate it directly, rather than passing it as parameters. – Mustafa sabir Jun 11 '14 at 06:25
  • Thank you so much for your help! So if I create a global list variable, I should be able to use methods to add names, remove names, sort the names, etc, as many times as I want before exiting the program? Or I could keep the program the same as it is now and try returning new arrays in my methods? I can take the original array, call addNames(), convert the array to a list, add names, convert the list to a new array, return the new array, and then set the old array equal to the new array. Thanks for the heads up, I'll play around with this some more so that I can add names without losing any – HandleThatError Jun 11 '14 at 06:40
  • Happy to help. Check my answer, hope it cleares your doubt, otherwise leave a comment there. – Mustafa sabir Jun 11 '14 at 06:48

6 Answers6

1

Check whether the input is of type int before processing or do the required exception handling before handing an input.

Use Scanner's hasNextInt() to validate whether the input is int.

Scanner input = new Scanner(System.in);
while (!input.hasNextInt()) {
   System.out.println("Please enter int values only");
   input.nextLine();
} 

else you can also do some exception handing to throw exception if int is not provided

try {
        Scanner input = new Scanner(System.in);
        int userChoice = input.nextInt();
    } catch (Exception e) {
        System.out.println("Please re-enter");
    }

To convert to arrays

Arrays.asList(string.toCharArray());

and if you want to get back as a string array

ArrayList<String> myList = new ArrayList<String>();
myList.add("value1");
myList .add("value2");
String[] myStringArray = new String[myList.size()];
myStringArray = myList.toArray(myStringArray);
AurA
  • 12,135
  • 7
  • 46
  • 63
0

Java is pass by value always so there is no pass by reference, but in most cases your value is a reference to your object.

For example if you add an element to a List that is passed to a method, you can see the element in the caller method because the value that has been passed to the method was in fact a reference to the list.

The issue you have is in this method:

public static void addName(String[] nameBag){
    ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(nameBag));
    Scanner input = new Scanner(System.in);
    System.out.println("What name do you want to add to the pack?");
    String userSelection;
    userSelection = input.nextLine();
    nameList.add(userSelection);
    nameBag = nameList.toArray(nameBag);
}

You have your array in the main method called nameBag let's this is reference(1).

You call your addName(nameBag(1)) (I'll mark the objects' reference in brackets)

Then you will do your first line

ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(nameBag(1)));

still using the same nameBag. Then do a lot of stuff with the ArrayList which is completely fine but at the moment you do

nameBag = nameList.toArray(nameList); //I assume a typo here

your nameBag will become nameBag(2) because you will put an other array reference into the same variable so from this moment you don't have any reference to nameBag(1) in your method. The easiest way to stop these kind of programming errors is to have your method parameters declared final.

What you can do with nameBag is that you can assign a value to an index so if you did nameBag[1] = "foo" that would've been visible in the caller, but you cannot reassign the value, because from that moment on you're working on a completely different object.

What you could do is either don't use arrays rather use List but given this is an array exercise just return the nameBag you created in the last line and ressign your array in the main method

maczikasz
  • 1,113
  • 5
  • 12
0

Try this code it works well. You have made several mistakes which is why it was not working. I have kept your code as commented, it will be convenient to understand.

Keep this points in mind. It will help I hope: 1. Try to use List or Set instead of Arrays, as Arrays are not of dynamic size 2. Tree set will be sorting content by default 3. Dont pass value to a method when its not required

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;

public class nameBag {
static Set<String> nameBagSet = new TreeSet<String>();
public static void main(String[] args) {

    //String[] nameBag = new String[25];

    Scanner input = new Scanner(System.in);

    while (true){

        System.out.println("Welcome to the bag of names.");
        System.out.println("1. Add an item to the bag.");
        System.out.println("2. Display items in bag.");
        System.out.println("0. Exit program.");

        int userChoice = input.nextInt();
        switch (userChoice){
                case 1:
                addName();
                break;
            case 2:
                displayNames();
                break;
            case 0:
                System.out.println("See ya!");
                System.exit(0);
        }
    }



}

public static void addName(){
//    ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(nameBag));

Scanner input = new Scanner(System.in);
System.out.println("What name do you want to add to the pack?");
String userSelection;
userSelection = input.nextLine();
nameBagSet.add(userSelection);
//    nameList.add(userSelection);
//    nameBag = nameList.toArray(nameBag);
 }

 public static void displayNames(){
 for (String s : nameBagSet) {
    if (!(s == null)) {
        System.out.println(s);
    }
}

} }

  • Although this would work it does not match the user's requirement " I have a function that takes the array, converts it into a list, attempts to add a name of the user's choice, and then attempts to convert that list BACK to the array that it once was" – Mustafa sabir Jun 11 '14 at 06:36
  • Yes I have done it with Arraylist instead of using arrays, because its an interactive program, where user can give input as many as he wants. User should not be bound to give only 25(as 25 was there in the programming) inputs. This I have mention in answer also. @Mustafa – exceptionHandler Jun 11 '14 at 09:06
0

The below line in your addName method does not have any effect in the actual execution. nameBag = nameList.toArray(nameBag);

The reason is, you are really changing changing your local variable "nameBag" to point to a new array. This has no effect on the variable that is defined in the main function.

To really understand what is happening, change the variable name of your addName to something else. For e.g. paramNameBag. You will immediately understand what is going on. When you make such a change, you would want the above line to be really referring to the variable defined in the main method. But you cannot access that here because it is local to the main method. Since you are using all static method, the only way you can achieve that is by moving nameBag declaration out and making it static.

There is another quick and dirty solution. Change the addName signature to below and return the array from the method. Then you can reassign the returned array to the main variable.

 public static String[] addName(String[] nameBag, String userSelection){
    .....
    .....
    return nameList.toArray(nameBag);
 }



    switch (userChoice){
                case 1:
                namesBag = addName(nameBag);

This should do it.

But I really recommend just using a ArrayList in the first place instead of array and converting back and forth to ArrayList.

Good Luck

Raghu
  • 351
  • 1
  • 4
0

Not going into good coding conventions, you had two mistakes in your program

1st displayNames(nameBag 

which you seem to have corrected in edit

and 2nd Mistake is related to understanding parameters in java. You cannot pass nameBag as a parameter and add values to those parameters. The values will get lost each time function addName() ends execution. You can declare a static variable nameBag under the class variables and manipulate it directly, rather than passing it as a parameter. This way your changes reflect even if addName() displayName() functions end execution (as long as your program is running). Plus you dont need any return type. You can simply access the global List variable directly.

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Arrays;

public class HelloWorld{

static String[] nameBag; // NameBag is declared as class variable

static{

    nameBag = new String[25]; //initialize nameBag

}

    public static void main(String[] args) {


        Scanner input = new Scanner(System.in);

        while (true){

            System.out.println("Welcome to the bag of names.");
            System.out.println("1. Add an item to the bag.");
            System.out.println("2. Display items in bag.");
            System.out.println("3. Exit program.");
              int userChoice=0;
             try{
           userChoice = input.nextInt();
             }
             catch(Exception e){
                 e.printStackTrace();

             }
            switch (userChoice){
                    case 1:
                    addName(); //No need to pass nameBag as a parameter
                    break;
                case 2:
                    displayNames(); //No need to pass nameBag as a parameter
                    break;
                case 3:
                    System.out.println("See ya!");
                    System.exit(0);
            }
        }



    }

public static void addName(){
    //everything is same here, just accessing nameBag directly
    Scanner input = new Scanner(System.in);
    System.out.println("What name do you want to add to the pack?");
    String userSelection;
    userSelection = input.nextLine();
    ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(nameBag));
    nameList.add(userSelection);
    nameBag = nameList.toArray(nameBag);
}

public static void displayNames(){
//everything is same here, just accessing nameBag directly
    for (String s : nameBag) {
        if (!(s == null)) {
            System.out.println(s);
        }
    }
}
}
Mustafa sabir
  • 4,130
  • 1
  • 19
  • 28
  • Thank you very much for your help! This does help a lot. I'll work on the solution using these tips. The assignment seems to require that we pass an array to the methods. Not sure how exactly they'd want that to be solved but I'll work on a version of the program that solves the problem by using a static variable. After I finish the program that way, I'll research more on how to do it the way they're asking. Thanks so much for your help! – HandleThatError Jun 11 '14 at 06:57
  • You're welcome. BTW You can pass array parameter to displayNames(){} function, or any function that will just read the parameter. displayNames(bagName) would work fine. Its just that java is pass by value and hence you cannot modify the original passed params (bagName from main method). So for functions like addName() you have to opt for a global list to persist the changes. If there is a requirement like something you say, i think it would be like you take input from users in an array and pass that array to addName(bagName), which inturn adds BagName contents to a global Array. – Mustafa sabir Jun 11 '14 at 07:06
0

There is no simple way of sending a Class-based object by reference, all classes are passed in such a way that the reference to them is passed as a parameter copied as value.

To circumvent that, you can use a Holder object that holds the pointer and that way what is sent as a reference is the pointer to the Holder object, but the object held inside it is actually the pointer to the object you want to manipulate.

So instead of:

public class nameBag 
{
    public static void main(String[] args) 
    {
       String[] nameBag = new String[25];

You would have:

public class ArrayHolder
{
    private String[] nameBag;

    public ArrayHolder(String[] nameBag)
    {
        this.setNameBag(nameBag); 
    }

    public String[] getNameBag()
    {
        return this.nameBag;
    }

    public void setNameBag(String[] nameBag)
    {
        this.nameBag = nameBag;
    }
    //by the way, if these functions break your limitations,
    //then it works even if nameBag is just a simple public variable
}

And then you could have:

public class nameBag 
{
    public static void main(String[] args) 
    {
       String[] nameBag = new String[25];
       ArrayHolder arrayHolder = new ArrayHolder(nameBag);
       ...

But then you would have to send the ArrayHolder to your functions:

public static void addName(ArrayHolder arrayHolder)
{
    //everything is same here, just accessing nameBag directly
    Scanner input = new Scanner(System.in);
    System.out.println("What name do you want to add to the pack?");
    String userSelection = input.nextLine();
    ArrayList<String> nameList = new ArrayList<String>(Arrays.asList(arrayHolder.getNameBag()));
    nameList.add(userSelection);
    nameBag = nameList.toArray(new String[nameList.size()]);
}

But as you stated initially, the Array must be the parameter, and it can't be your own ArrayHolder object; and with that premise I guess the only solution really is that you need to make it a static variable.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428