Hello,

I'm refactoring a C++ program into a Java program (yes, it's homework). I have a problem where I add objects to an ArrayList of type Student, but when I try to access the objects, every valid index returns what should be only in the last element.

For the sake of brevity, I'm posting my code with most of the irrelevant parts removed. I'll be happy to post the whole thing if there isn't enough to go on here.

package project5;

import java.io.*;
import java.util.*;

public class StudentAction
    {

    ArrayList<Student> student = new ArrayList<Student>();
    Scanner s;
    // other class variable declarations

    public void LoadStudents()
        {

        // code to check existence and readbility of target file: seems to work

        while (s.hasNextLine())
            {
            // code to load data from file with Scanner object - seems to work

            // code to put the assembled subclasses into the Student object

            // prints correct values here
            System.out.println(tempPerson.getLastName());

            // this assembles the sub objects into each Student object
            // in the student ArrayList
            student.add(new Student(tempPerson, tempCompDate, gpa, credits));

            //prints correct values here
            System.out.println(student.get(counter).getPerson().getLastName());
            System.out.println(counter);
            counter++;
            } // end while

        for (int a = 0; a < student.size(); a++)
            {
            // returns the value that should be in
            // in the last element 50 times.
            // there should be 50 elements, but they
            // should have mostly different values
            System.out.println(student.get(a).getPerson().getLastName());
           } 												

        } // end function LoadStudents
    // other functions
    } // end class StudentAction

I hope someone can show me where I've erred. Thank you.

Your LoadStudents method doesn't make sense to me. You said while(s.hasNextLine()) but you never read anything in, so I'd think that would be an infinite loop. Additionally, you kept adding a new Student with the argument 'tempPerson', but tempPerson doesn't seem to be changed anywhere. You'll need to post more code...

Your LoadStudents method doesn't make sense to me. You said while(s.hasNextLine()) but you never read anything in, so I'd think that would be an infinite loop. Additionally, you kept adding a new Student with the argument 'tempPerson', but tempPerson doesn't seem to be changed anywhere. You'll need to post more code...

OK. I left a lot out to try to just show where the problem was. Here's the class through the end of the first function. The other two functions depend on the data from the first, so I'm leaving them out for now instead of posting 250 lines of code. Please let me know if you need the whole class. Thanks.

package project5;

import java.io.*;
import java.util.*;

public class StudentAction
    {

    ArrayList<Student> student = new ArrayList<Student>();
    Person tempPerson = new Person();
    Name tempName = new Name();
    Address tempAddress = new Address();
    Date tempBirthDate = new Date();
    Date tempCompDate = new Date();
    String lastName, firstName, middleInitial, suffix,
            addLine1, addLine2, city, state, zip,
            birthDayMonthStr, birthDayDateStr, birthDayYearStr,
            CompletionMonthStr, CompletionDayStr, CompletionYearStr,
            gpaStr, creditsStr;
    int BirthDayMontInt, BirthDayDateInt, BirthDayYearInt,
            CompletionMonthInt, CompletionDayInt, CompletionYearInt, credits;
    double gpa;
    Scanner s;

    public void LoadStudents()
        {

        // define file for input
        File inputFile = new File(System.getProperty("user.home") + "/data/StudentList.txt");

        // if file properties are OK...
        if (inputFile.exists() && inputFile.isFile() && inputFile.canRead())
            {
            try
                {
                // ... open the file
                s = new Scanner(new BufferedReader(new FileReader(inputFile)));
                } // end try
            catch (FileNotFoundException fnfe)
                {
                System.out.println("File read error: " + fnfe.getMessage());
                System.exit(-1);
                } // end catch
            } // end if
        else
            {
            System.out.println("Cannot open \"StudentData.txt\". Please ensure\n" + "that it is in the \"data\" subfolder of the\n" + "folder containing the executable\n" + "and that file permissions are correctly set.");
            System.exit(-1);
            } // end else

        // get data, line by line
        int counter = 0;
        while (s.hasNextLine())
            {
            lastName = s.nextLine(); // last name
            firstName = s.nextLine(); // first name
            middleInitial = s.nextLine(); // middle initial
            suffix = s.nextLine(); // suffix
            addLine1 = s.nextLine(); // address line 1
            addLine2 = s.nextLine(); // address line 2
            city = s.nextLine(); // address city
            state = s.nextLine(); // address state
            zip = s.nextLine(); // address zip
            birthDayMonthStr = s.nextLine(); // birthday month
            birthDayDateStr = s.nextLine(); // birthday day
            birthDayYearStr = s.nextLine(); // birthday year
            CompletionMonthStr = s.nextLine(); // est. completion month
            CompletionDayStr = s.nextLine(); // est. completion day
            CompletionYearStr = s.nextLine(); // est. completion year
            gpaStr = s.nextLine(); // grade point average
            creditsStr = s.nextLine(); // credit hours completed

            // convert birth date Strings to ints
            BirthDayDateInt = Integer.parseInt(birthDayDateStr.trim());
            BirthDayMontInt = Integer.parseInt(birthDayMonthStr.trim());
            BirthDayYearInt = Integer.parseInt(birthDayYearStr.trim());

            // convert completion date Strings to ints
            CompletionDayInt = Integer.parseInt(CompletionDayStr.trim());
            CompletionMonthInt = Integer.parseInt(CompletionMonthStr.trim());
            CompletionYearInt = Integer.parseInt(CompletionYearStr.trim());

            // convert credit Strings to ints
            credits = Integer.parseInt(creditsStr.trim());

            // convert gpa String to double
            gpa = Double.parseDouble(gpaStr.trim());

            // assemble the data into subclasses
            tempName.setName(firstName, middleInitial, lastName, suffix);
            tempAddress.setAddress(addLine1, addLine2, city, state, zip);
            tempBirthDate.setDate(BirthDayDateInt, BirthDayMontInt, BirthDayYearInt);
            tempPerson.setPerson(tempName, tempAddress, tempBirthDate);
            tempCompDate.setDate(CompletionDayInt, CompletionMonthInt, CompletionYearInt);

            // put the assembled subclasses into the student class

            // debug line
            System.out.println(tempPerson.getLastName()); // <-- OK here

            student.add(new Student(tempPerson, tempCompDate, gpa, credits));

            // debug line, data correct here
            System.out.println(student.get(counter).getPerson().getLastName());

            counter++;

            // debug line, data correct here
            System.out.println(counter);

            } // end while

        // debug for block
        // PROBLEM: prints the string that should be in the last element
        // for every element. 
        for (int a = 0; a < student.size(); a++)
            {
            System.out.println(student.get(a).getPerson().getLastName());
            }

// @TODO put try block here
        s.close(); // close the file

        }// end function loadStudents
} // dummy close of class, not in real code at this point

Yes, but you didn't say what the error is. The code seems fine. What is your problem?

Also you should post a few lines of the file you are reading.
Also check the API for the class: Scanner:
http://java.sun.com/j2se/1.5.0/docs/api/overview-summary.html
You will find it at the link: java.util at the left column.

Its method: nextLine returns the entire line, so I assume that the file you are reading is like:

lastName
firstName
middleInitial
..
..


..
lastName
firstName
middleInitial

(Note: The plain code tags that I'm using for my flat file are defaulting to Java syntax. I don't know why that's happening.)

You're right about my file format. It's pretty long, so here are the first two and last two sets of data in it, with all of the lines in the middle omitted (the blank lines are intentional, for N/A data):

SMITH 
Jacob
A

1 1ST STREET
APT A
INDIANAPOLIS
IN
46200
11
12
1990
5
15
2010
3.00
32
JOHNSON 
Michael
B

2 2ND STREET

INDIANAPOLIS
IN
46201
2
30
1989
12
15
2011
2.89
89
TURNER 
Aiden
W

9 9TH LN
APT C
INDIANAPOLIS
IN
46248
5
8
1990
5
15
2010

78
TORRES 
Isaac
X

10 10TH LN

INDIANAPOLIS
IN
46249
4
26
1992
12
15
2010

37

The text file contains a purposefully unsorted list of records, which start with a last name, all the other data that goes with that last name in sequential order, then another last name, repeat, etc. In my data file, the first set starts with the last name SMITH, then there are 48 other sets of data that start with the other last names until finally the last set of data, which starts with the last name TORRES.

I don't get a runtime error. Scanner() seems to be pulling the data in correctly, but when I try to iterate through the objects in the ArrayList, after (but not before) the end of the while block, all of the objects returned are the same as the object that should only be in the last element.

In my second code posting, debug line 99 outputs the correct lastName for each Person object that I'm about to feed into my Student ArrayList: System.out.println(tempPerson.getLastName()); // <-- OK here On line 101, the sub-objects are fed into a new Student constructor and the resulting Student object is added to the ArrayList of Students: student.add(new Student(tempPerson, tempCompDate, gpa, credits)); I put another debug line (104) that shows that the lastName property of the Person object is correct in each Student element of the ArrayList after filling the Student ArrayList, before the end of the while block.

The problem shows up in my debug for block starting on line 116 (after the end or the while block on line 111):

for (int a = 0; a < student.size(); a++)
            {
            System.out.println(student.get(a).getPerson().getLastName());
            }

prints the last name "TORRES" 50 times, which is the number of records that I have in the data file.

When I run the program (with the rest of the classes that are omitted here) to completion, I get the debug lines, then I get 50 records that are exactly the same (with the TORRES data), formatted the way that they are supposed to be according to the assignment.

I refactored this code from an old C++ program that I wrote, which ran fine. I think that the idea is to learn the equivalent Java constructs in place of the C++ ones.

If it helps any, I had the same problem using Student[] arrays before I changed to code to use ArrayLists.

I'm sorry this was so long. I'd really appreciate it if anyone can help. This has been driving me crazy all weekend. Thank you.

Your problem lies in the nature of variable values and pointers.

Once you have input the data, you save into the array using a new instance of the Student object class. This is the correct way to get separate instances of the student variable. Each instance refers to the collection of temp objects created in the main outer block which are in turn built from the lines of input data.

After inputting the final lines, you input them into the temp objects, tempname etc. then collect them into the final instance of Student. This refers to the temp objects as do all the previous student instances.

As with student, you need to create new instances of the temp objects within the while loop to hold each student record entered!

Because you only have ONE tempPerson object. You have the same instance of it:

tempPerson.setPerson(tempName, tempAddress, tempBirthDate);

System.out.println(tempPerson.getLastName()); // <-- OK here

student.add(new Student(tempPerson, tempCompDate, gpa, credits));

System.out.println(student.get(counter).getPerson().getLastName());

The last system.out at the above code prints the name correctly because it is the last one read from the loop. Then when you loop again you replace the name in the tempPerson object, and you put the latest in the list. Then you print what you read and you see correct results.
But
You put in the Student object the tempPerson. So when you do this: tempPerson.setPerson(tempName, tempAddress, tempBirthDate); you change the value of tempPerson.
Since in java all the objects are passed by reference ALL the student objects you created have the SAME tempPerson instance.
So when you change the tempPerson, it changes that tempPerson in all the Students that are in the ArrayList.

Inside the loop you create a new Student each time before you put it in the list: student.add(new Student(tempPerson, tempCompDate, gpa, credits)); Do the same for the rest:

tempName = new ....();
tempAddress = new ....();
tempBirthDate = new ....();
tempPerson = new ....();
tempCompDate = new ...();

tempName.setName(firstName, middleInitial, lastName, suffix);
tempAddress.setAddress(addLine1, addLine2, city, state, zip);
tempBirthDate.setDate(BirthDayDateInt, BirthDayMontInt, BirthDayYearInt);
tempPerson.setPerson(tempName, tempAddress, tempBirthDate);
tempCompDate.setDate(CompletionDayInt, CompletionMonthInt, CompletionYearInt);
This question has already been answered. Start a new discussion instead.