JAVA Lists and Files Objectives • Be able to use ArrayLists: • Create • Initialize • Add elements • Retrieve elements • Pass as arguments to methods • Return from methods • Be able to use Random to get a random number • Be able to parse input from a file • Be able to write output to a file 3 Example: Analysis and Design • We’d like to make a guessing game that drills students on the countries of the world. • Elements: • A GUI controller; Some hint goes here (maybe an image and/or text)… • A Game class that: • Represents a list of countries; • Randomly chooses a country as the current answer; • Produces a sequence of hints. • A Country class that: • Represents stuff about countries. Your guess… Give up 4 Limitations of Arrays • Our initial iteration assumes that: • We know how many countries there are; • We’re happy with the low-level array methods. • Moving beyond these assumptions forces us to give up arrays because arrays: • Are fixed in size at compile time; • Have a limited set of predefined methods. 5 Lists • Java’s List data structure is more flexible: • Lists can grow or shrink at run time. • Lists provide more predefined behaviors. • The Java Collections framework provides classes supporting groups of objects: • List<> specifies an interface for an ordered collection of typed objects; • ArrayList<> implements the List<> interface using an array. 6 The ArrayList Class ArrayLists store an array of typed objects. List<aType> aList = new ArrayList<aType>(); aList size 0 array 7 ArrayLists: Adding Values (add) ArrayLists handle their own memory allocation. List<aType> aList = new ArrayList<aType>(); aList.add(aTypeObject); size array 1 [0] [1] … [m-1] aList aTypeObject 8 ArrayLists: Accessing Values (get) ArrayLists provide indexed access. List<aType> aList = new ArrayList<aType>(); aList.add(aTypeObject); System.out.println(aList.get(arrayIndex)); aList size array 1 aTypeObject [0] [1] … [m-1] 9 ArrayLists: Memory Allocation ArrayLists allocate memory automatically. List<aType> aList = new ArrayList<aType>(); aList.add(aTypeObject); System.out.println(aList.get(arrayIndex)); aList.add(a2ndTypeObject); ... aList.add(anM+1stTypeObject); aList size array [0] [1] … [m-1][m] … m+1 aTypeObject a2ndTypeObject anM+1stTypeObject Random • To get a random number in Java, create a random number generator: Random randomGenerator = new Random(); • The generator will return a new random number each time it is asked to do so: int randomNumber1 = randomGenerator.nextInt(); int randomNumber2 = randomGenerator.nextInt(15); double randNumber3 = randomGenerator.nextDouble(); Using lists for our example • We’d like to have an arbitrary number of countries in the “list”. • We’ll keep the same GUI controller, but will create an arbitrarily long List of country name Strings. • Upgrade the CountryGuessGame to use lists rather than arrays. The results of this programming should be as follows: • CountryGuessController1 should not change; • CountryGuessGame should end up looking like CountryGuessGame1 (handout). package c12lists.lecture.countryguess; import java.util.ArrayList; import java.util.List; import java.util.Random; public class CountryGuessGame { private List<String> myCountries; private int myAnswerIndex, myHintCount; private Random myRandom; public CountryGuessGame() { myCountries = new ArrayList<String>(); myCountries.add("Honduras"); myCountries.add("Panama"); myRandom = new Random(); reset(); } public void reset() { myAnswerIndex = myRandom.nextInt(myCountries.size()); myHintCount = 0; } //continued on next slide //continued from previous slide public boolean guess(String text) { return myCountries.get(myAnswerIndex).equalsIgnoreCase(text); } public String getAnswer() { return myCountries.get(myAnswerIndex); } public String getHintText() { myHintCount++; String name = myCountries.get(myAnswerIndex); if (myHintCount == 1) { return "The name starts with '" + name.charAt(0) + "'"; } else if (myHintCount == 2) { return "The name has " + name.length() + " letters"; } else if (myHintCount == 3) { return "The name ends with '" + name.charAt(name.length() - 1) + "'"; } else { return "no more hints"; } } } //ends CountryGuessGame class 14 Array & Lists Syntax String[] myCountries = new String[2]; myCountries[0] = "Honduras"; myCountries[1] = "Panama"; System.out.println(myCountriesArray.length); System.out.println(myCountriesArray[0]); List<String> myCountriesList = new ArrayList<String>(); myCountriesList.add("Honduras"); myCountriesList.add("Panama"); System.out.println(myCountriesList.size()); System.out.println(myCountriesList.get(0)); 15 Modeling Countries CountryGuessController CountryGuessGame +myAnswerIndex +myHintCount CountryGuessTest +getHintText() +guess() +reset() Country 1 * +myName +myContinent 16 ArrayLists: As Parameters ArrayLists can be passed as parameters. private int count(List<Country> countries, String continent) { int result = 0; for (int i = 0; i < countries.size(); i++) { if (countries.get(i).getContinentName() .equalsIgnoreCase(continent)) { result++; } } return result; } 17 ArrayLists: As Return values ArrayLists can be returned as return values. private List<Country> loadCountries() { List<Country> result = new ArrayList<Country>(); result.add(new Country("Algeria", "Africa")); result.add(new Country("Angola", "Africa")); ... return result; } 18 ArrayList: Copying List<Country> original = new ArrayList<Country>(); // add two country objects to original (c1 & c2)… List<Country> referenceCopy = original; original size array 2 referenceCopy c1 c2 List<Country> shallowCopy = (List<Country>)original.clone(); shallowCopy size 2 array 19 List<Country> deepCopy = deepCopy(original); deepCopy size array 2 c1copy c2Copy public List<Country> deepCopy(List<Country> original) { List<Country> result = new ArrayList<Country>(); for (int i = 0; i < original.size(); i++) result.add(new Country(myCountries.get(i).getName(), myCountries.get(i).getContinentName(), myCountries.get(i).getImageName())); return result; } 20 ArrayList Equality Similar issues arise when checking arraylist equality: • anArrayList.equals(anotherArrayList) checks the two lists are the same size and that their corresponding elements are equals(). • This works for lists of strings, but special equality checking routines must be written for lists of other types. • The String class has an equals() operator that checks string equality properly. 21 Multi-Dimensional Lists • Lists can also be multi-dimensional. • Declaring 2-D lists: ArrayList<ArrayList<RType>> ID • Initializing 2-D lists: new ArrayList<ArrayList<RType>>(rowsize) • Accessing 2-D array elements: ID.get(row).get(column) • Multidimensional arrays are generally easier to use and more efficient. 22 Multi-dimensional List Structures • Multi-dimensional lists are useful for more general multi- dimensional structures. • Example: Text +title: String 1 0..* Paragraph 1 0..* Sentence Word +value: String 0..* 1 23 Example: Character Drill • We’d like to modify the guessing game so that drills students on Chinese Characters. • A sketch of a solution achieving this goal is shown here. Some hint goes here (maybe an image and/or audio)… User’s guess… Give up 24 Example: Design • The design includes the following classes: CharacterPanel CharacterDrillController +setImage() +setPronunciation() CharacterDrill +myAnswerIndex +myHintCount CharacterDrillTest +guess() +reset() +getHintImageFilename() +getHintPronunciationFilename() +getHintText() Character 1 * +myTranslation +myPinyin +myImageFilename +myPronunciationFilename +mySentence 25 Limitations of Hard-Coding Data • Our initial iteration assumes that: • we can code the data directly in the program; • the data never (or rarely) changes; • people who change data know how to program. • This approach does not scale well to real data-based applications. 26 Input & Output Streams • Input and output in Java is accomplished using stream classes: Input Stream Program Output Stream Program 27 Java Streams • Simple I/O uses predefined streams: • System.in • System.out • System.err • Create file streams using: • File • Scanner • PrintWriter 28 File • The File class models a system-independent view of a file comprising: • Filename; • Directory pathname: • Relative; • Absolute. • Patterns: new File(pathAndFilenameString) new File(pathnameString, filenameString) 29 Scanner • The Scanner class can scan: • Keyboard input stream; • File; • String. • Pattern: new Scanner(inputStreamOrFileOrString) • The API includes these methods: • next() nextInt() nextLine() ... • hasNext() hasNextInt() hasNextLine() ... • close() Example: Compute Statistics Problem: Compute statistics for a file of quiz scores. Given: the data filename and path Algorithm: • Open a read stream/scanner to the given file. • While the file has more tokens: • Read the token. • Process the token. • Close the file stream/scanner. Statistics Solution Structure import import import import import java.io.File; java.io.FileNotFoundException; java.util.ArrayList; java.util.List; java.util.Scanner; public class Statistics { public static final String PATH = “src/c11files/examples/data/”; //main method will go here ... //compute methods will go here ... } Method implementations on next slides public static void main(String[] args) { Scanner keyboard = new Scanner(System.in); System.out.print("Data filename: "); String filename = keyboard.next(); try{ Scanner fileIn = new Scanner(new File(PATH, filename)); List<Integer> scores = new ArrayList<Integer>(); while (fileIn.hasNext()) { scores.add(fileIn.nextInt()); } fileIn.close(); System.out.println(scores); double average = computeAverage(scores); System.out.printf("average: %5.2f%n", average); System.out.printf("std dev: %5.2f%n", computeVariance(scores, average)); } catch(FileNotFoundException e){ System.out.println("Invalid file indicated."); } } public static double computeVariance( List<Integer> values, double average) throws Exception { if (values == null || values.size() == 0) { throw new Exception("Empty list"); } int sum = 0; for (int i = 0; i < values.size(); i++) { sum += Math.pow(values.get(i) - average, 2); } return Math.sqrt(sum / values.size()); } 34 public static double computeAverage(List<Integer> values) throws Exception { if (values == null || values.size() == 0) { throw new Exception("Empty list"); } int sum = 0; for (int i = 0; i < values.size(); i++){ sum += values.get(i); } return sum / values.size(); } 35 Example: Record Input Problem: Read in soldier data from a file. Soldiers may or may not have nickname(s). Given: the data filename and path Algorithm: Open a read stream/scanner to the given file. While the file has more lines: Read the line. Process the fixed and variant tokens. Close the file stream/scanner. Soldier ReadRecordsConsole +myName +myRank +mySerialNumber +myNicknames +Soldier(String) +toString() public class ReadRecordsConsole { public static final String PATH = "src/c11files/examples/data/"; public static void main(String[] args) { try{ Scanner keyboard = new Scanner(System.in); System.out.print("Data Records filename: "); String filename = keyboard.next(); Scanner fileIn = new Scanner(new File(PATH, filename)); List<Soldier> soldiers = new ArrayList<Soldier>(); while (fileIn.hasNextLine()) { soldiers.add(new Soldier(fileIn.nextLine())); } fileIn.close(); for (int i = 0; i < soldiers.size(); i++) { System.out.println(soldiers.get(i)); } } catch(FileNotFoundException e){ System.out.println("Invalid File indicated."); } } } public class Soldier { private String myName, myRank, mySerialNumber; private List<String> myNickNames; public Soldier(String line) { Scanner scanner = new Scanner(line); myName = scanner.next(); myRank = scanner.next(); mySerialNumber = scanner.next(); myNickNames = new ArrayList<String>(); while (scanner.hasNext()) { myNickNames.add(scanner.next()); } scanner.close(); } public String toString() { String result = myName + " " + myRank + " " + mySerialNumber; for (int i = 0; i < myNickNames.size(); i++) { result += " " + myNickNames.get(i); } return result; } } 38 PrintWriter • The PrintWriter class can print formatted text to a text-output stream. • Pattern: new PrintWriter(outputFile) • The API includes these methods: • print() println() ... • printf() • close() flush() 39 Example: File Output Problem: Get data from the user, put it into a file. Given: the output filename and path Algorithm: Open a print writer stream to the given file. Loop forever: Prompt for and read a line of data. If the line is the sentinel Quit. else Output the line. Close the file stream/scanner. 40 Example Implementation PrintWriter fileOut = new PrintWriter(new File(path, filename)); String line = ""; while (true) { System.out.print("enter record (just enter to quit): "); line = keyboard.nextLine(); if (line.equals("")) { break; } else { fileOut.println(line); } } fileOut.close(); System.out.println("data stored to: " + path + filename); Character Drill Revisited CharacterPanel CharacterDrillController +setImage() +setPronunciation() CharacterDrill +myAnswerIndex +myHintCount CharacterDrillTest +guess() +reset() +getHintImageFilename() +getHintPronunciationFilename() +getHintText() Character 1 * +myTranslation +myPinyin +myImageFilename +myPronunciationFilename +mySentence