Search code examples
javastringloopsvariables

Using st.countTokens() in my Java code doesn't work as intended


I am coding in Java and trying to use a while loop to add all of the values of a string to an array that were set up as tokens separated by commas. At some point, the values just turn into 0, and I don't know why.

My array is a double array called "prices". My string is named "values". The loop I am using is this:

StringTokenizer st = new StringTokenizer(values,",");
prices = new double[st.countTokens()];
for (int i = 0; i < st.countTokens(); i++) {
     token = st.nextToken();
     double price = Double.parseDouble(token);
     prices[i] = price;
}

When I use 1, 2, 3, 4, 5, 6, 7, 8, and 9 as the inputs, the output that I expect is to have prices[0] through prices[8] be equal to the 9 values that I input for the tokens, but this is the output I get:

Please enter the price values using numbers and decimals only!
Input 0 at any time to stop entering values!
Enter the price value: 1
Enter the price value: 2
Enter the price value: 3
Enter the price value: 4
Enter the price value: 5
Enter the price value: 6
Enter the price value: 7
Enter the price value: 8
Enter the price value: 9
Enter the price value: 0

Show prices array:
prices[0] = 1.0
prices[1] = 2.0
prices[2] = 3.0
prices[3] = 4.0
prices[4] = 5.0
prices[5] = 0.0
prices[6] = 0.0
prices[7] = 0.0
prices[8] = 0.0

Here is my entire code:

import java.util.Scanner;
import java.util.StringTokenizer;

class ArrayInputShow {

    public static void main (String args[]) {

        int count = 0, numTokens = 0;
        double prices[], temp;
        String values = "", tempInput = "", token = "";
        Scanner input = new Scanner(System.in);

        System.out.println("Please enter the price values using numbers and decimals only!");
        System.out.println("Input 0 at any time to stop entering values!");
        while (true) {
            if (count != 0) {
                values = values + ",";
            }
            System.out.print("Enter the price value: ");
            tempInput = input.nextLine();
            if (tempInput.equals("0")) {
                break;
            }
            values = values + tempInput;
            count++;
        }
        StringTokenizer st = new StringTokenizer(values,",");
        numTokens = st.countTokens();
        prices = new double[st.countTokens()];
        for (int i = 0; i < st.countTokens(); i++) {
            token = st.nextToken();
            double price = Double.parseDouble(token);
            prices[i] = price;
        }

        System.out.println(""); //break
        System.out.println("Show prices array:");
        for (int i = 0; i < prices.length; i++) {
            System.out.println("prices[" + i + "] = " + prices[i]);
        }

        System.out.println(""); //break
        System.out.println("Show prices array backwards:");
        for (int i = prices.length - 1; i >= 0; i--) {
            System.out.println("prices[" + i + "] = " + prices[i]);
        }

        System.out.println(""); //break
        System.out.println("Show prices array sorted from least expensive to most expensive:");
        for (int j = 0; j < prices.length - 1; j++) {
            for (int i = 0; i < (prices.length - (1 + j)); i++) {
                if (prices[i] > prices [i + 1]) {
                    temp = prices[i + 1];
                    prices[i + 1] = prices[i];
                    prices[i] = temp;
                }
            }
        }
        for (int i = 0; i < prices.length; i++) {
            System.out.println("prices[" + i + "] = " + prices[i]);
        }
    }
}

This was my first attempt at making an array that declares and initializes depending on the users input and how many inputs the user gives.

I ended up finding a solution to this problem by declaring a variable "numTokens' and using that in place of using st.countTokens itself in the loop. However, I am still confused as to why it didn't work with st.countTokens. Isn't it the same thing?

This is what worked for me:

StringTokenizer st = new StringTokenizer(values,",");
numTokens = st.countTokens();
prices = new double[numTokens];
for (int i = 0; i < numTokens; i++) {
     token = st.nextToken();
     double price = Double.parseDouble(token);
     prices[i] = price;
}

But, like I said, I don't understand why this works, or how to avoid this problem in the future.


Solution

  • Your problem lies in these two lines:

    for (int i = 0; i < st.countTokens(); i++) {
        token = st.nextToken();
    

    With the st.nextToken(); you basically pop a value from the StringTokenizer, so the st.countTokens() will decrease every iteration. If you add a debug printline System.out.println(st.countTokens()); between the two lines above, you'll see it'll print 9,8,7,6,5, at which point i<5 will stop the loop.

    The most straight-forward fix is to put the st.countTokens(); in a variable before the loop:

    int countTokens = st.countTokens();
    for (int i = 0; i < countTokens ; i++) {
        token = st.nextToken();
    

    Although usually you'd simply use a while(st.hasMoreElements()) instead like this:

    int i = 0;
    while (st.hasMoreElements()) {
        token = st.nextToken();
        double price = Double.parseDouble(token);
        prices[i] = price;
        i++;
    }
    

    As for a general tip: I assume you're first reading all your prices and only then convert it to a double-array, because you don't know beforehand how many prices the user will input and how large the array will be. In that case, there is the ArrayList. :)

    The advantage with the ArrayList is also that you can use builtins for the reversing and sorting, so you won't have to do so manually, like this:

    import java.util.Scanner;
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Collections;
    
    class ArrayInputShow {
        public static void main (String args[]) {
            ArrayInputShow program = new ArrayInputShow();
    
            List<Double> prices = new ArrayList<Double>();
            Scanner input = new Scanner(System.in);
    
            System.out.println("Please enter the price values using numbers and decimals only!");
            System.out.println("Input 0 at any time to stop entering values!");
            while (true) {
                //System.out.print("Enter the price value: ");
                double price = input.nextDouble();
                if (price == 0) {
                  break;
                }
                prices.add(price);
            }
    
            program.printLinebreakAndMessage("Show given prices:");
            program.printPrices(prices);
    
            program.printLinebreakAndMessage("Show prices backwards:");
            Collections.reverse(prices);
            program.printPrices(prices);
    
            program.printLinebreakAndMessage("Show prices sorted from least expensive to most expensive:");
            Collections.sort(prices);
            program.printPrices(prices);
        }
    
        private void printLinebreakAndMessage(String msg) {
            System.out.println(); //break
            System.out.println(msg);
        }
    
        private void printPrices(List<Double> prices) {
          for (double price : prices) {
              System.out.println(price);
          }
        }
    }
    

    Try it online. (Although all inputs have to be given at once, so I've removed the System.out.print("Enter the price value: "); here.)