Here's the class definition for Student
public class Student { private String lastName, String firstName ; private double gpa ; public Student( String firstNameIn, String lastNameIn ) { lastName = lastNameIn ; firstName = firstNameIn ; } public void setGpa( double gpaIn ) { gpa = gpaIn ; } public double getGpa() { return gpa ; } }This is a simple class, but it serves its purpose.
This is the StudentList class definition.
public class Student { private ArrayList studentList = new ArrayList() ; public StudentList() { } public Student getStudent( int index ) { return (Student) studentList.get( index ) ; } public Student addStudent( Student stu ) { return studentList.add( stu ) ; } public int size() { return studentList.size() ; } }We want to add a method called getDeansList() which returns a new StudentList containing students with a GPA above 3.5.
There are some interesting issues on how to do this.
Here's an implementation.
public ArrayList getDeansList() { // Make new list StudentList newList = new StudentList() ; // studentList is an instance variable // Look at each student for ( int i = 0 ; i < studentList.size() ; i++ ) { Student s = studentList.getStudent( i ) ; // Filter based on GPA if ( s.getGpa() >= 3.5 ) newList.addStudent( s ) ; } }
Unlike a "real" filter, a filter program does not usually separate a list of items into two or more categories. Instead, it goes through one list, copies the object handles, adds it to a second list. The original list is not changed.
Suppose we went into studentList and modified the GPA for Jana Novotna to 3.7. If we found Java Novotna in the newList, and looked at the GPA, it would also be 3.7. That's because studentList and newList both have a handle to the same Student object.
We can make a deep copy where the entire object is copied. When this happens, we have two completely separate objects. If one of the objects is modified, the other is left alone.
Here's an example of the same method, but this time using a deep copy.
public ArrayList getDeansList() { // Make new list StudentList newList = new StudentList() ; // studentList is an instance variable // Look at each student for ( int i = 0 ; i < studentList.size() ; i++ ) { Student s = studentList.getStudent( i ) ; Student deepCopy = new Student( s.getFirstName(), s.getLastName() ) ; deepCopy.setGpa( s.getGpa() ) ; // Filter based on GPA if ( deepCopy.getGpa() >= 3.5 ) newList.addStudent( deepCopy ) ; } }This time, we made a deep copy. The local variable deepCopy constructs a completely new Student object, then copies the data from the original object to a new one.
Previously, we copied the object handle, which was a shallow copy.
It's more efficient to do shallow copies (which only copy handles) than deep copies (which usually requires a lot more copying).
Even though the object (balloon) can't change, you can assign the variable (which is a handle) to another balloon. But you can't modify the object.
If an object is immutable, then you don't really need deep copies. Deep copies are more "expensive" (they take more time) than shallow copies. Unfortunately, few classes are immutable, so you then have to decide whether you need a true deep copy, or whether copying handles and sharing the object is better.