• 2.0 Arrays


    Arrays are data structures which group together data that has a collective meaning under single variable. Arrays are fixed size collections of the same type of variables which can be either primitive values or objects. Individual values are accessed through a subscript (index) value generally starting at 0 for the first element:

    int[] array_name
    

    The previous code declares a variable of integer array type.

    A new operator is used to allocate the necessary memory locations:

    array_name = new int[5];
    

    Initial values can be supplied at the time of creation:

    int[] array_name = {1, 2, 3, 4, 5}
    

    The above array has 5 elements with the values 1-5. As previously mentioned 0 refers to the first element, i.e. array_name[0] refers to the first element and has value 1 while array_name[4] refers to the fifth element and has value 5.

    Array can also be multi-dimensional, i.e. can index more than one dimension of information. Multi-dimensional arrays have separate indexes for each dimension, e.g. a two dimensional array could be declared as:

    int[][] twoDArray = {{1, 2}, {3, 4}};
    

    Remembering that elements start as ZERO, twoDArray[1][0] refers to the second set of information in twoDArray[1][0] which is values {3,4} and the first element in that set twoDArray[1][0]. Therefore twoDArray[1][0] has value 3 (the first element of the second set).

    The class java.util.arrays has a number of array manipulation methods including copying, sorting and searching information.

    Arrays hold elements all of the same type and arrays of objects are indexed in the same way as with primitive data types. Attributes of individual objects are created/accessed/modified in the same way as stand-alone objects using constructors, accessors and mutators defined in the class of the object.

    To create a new array of objects:

    ClassName[] arrayOfObjects = new ClassName[5];
    

    Individual objects are created using class constructors

    arrayOfObjects[0] = new ClassName(x, y); 
    

    Field attributes accessed through getter methods

    arrayOfObjects[0].getX();
    

  • 2.1 Example - Playlist


    Example Playlist screenshot

    PlaylistItem is the object of interest with fields: name, time, artist, album etc:

    PlayList Item screenshot

    PlayList and PlaylistItem screenshot

    A Playlist object will contain a collection of PlaylistItem objects with operations to play, add and remove items etc. Note the class diagram doesn’t capture the nature of the composition association i.e. we are using an array.

    public class Playlist {
      // the playlist
      private PlaylistItem[] playlist;
      private int numberOfItems;
      private int currentlyPlayingIndex;
     
      // constant - the maximum number of items allowed
      private static final int MAX_ITEMS = 10;
      …
    }
    

    Note we are using an attribute to keep track of how many values are currently stored in the array.

    The playlist contains the following methods:

    Constructor:

    public Playlist() {
      this.playlist = new PlaylistItem[MAX_ITEMS];
      this.numberOfItems = 0;
      this.currentlyPlayingIndex = -1;
     }
    

    Getter:

    public PlaylistItem[] getPlaylist() { return this.playlist; }
    

    setter:

    public void setPlaylist(PlaylistItem[] playlist) { this.playlist = playlist; }
    

    addItem:

    public String addItem(PlaylistItem newItem) {
      if (this.numberOfItems < MAX_ITEMS) {
          this.playlist[this.numberOfItems++] = newItem;
          return "Succesful Addition";
    }
      return "Playlist Full - unsuccesful addition";        
    }
    

    Note we check to see if there is sufficient room before adding the new Object and incrementing the number of items.

    removeItem:

    public String removeItem(int itemNumber) {
       if (itemNumber <= numberOfItems) {            
           for (int i=itemNumber-1; i<numberOfItems-1; i++) {
                this.playlist[i] = this.playlist[i+1];
                this.playlist[i].setItemNumber(i+1);
    }
       this.numberOfItems--;
       return "Succesful Removal";
    }
       return "Item Does Not Exist - unsuccesful removal";        
    }
    

    Note we have to shuffle remaining items to ensure that objects are stored from index 0 onwards.

    searchByArtist:

    public Playlist searchByArtist(String artist) {
         Playlist searchResults = new Playlist();
         for (PlaylistItem p : this.playlist) {
              if (p.getArtist().equals(artist)) {
                  searchResults.addItem(p);
              }                
         }
         return searchResults;        
    }
    

    Note the for-loop used to iterate over the collection. A new Playlist object is created containing only those PlaylistItems which match the search criteria.
  • 2.2 I/O Streams


    I/O Streams (Input / Output streams) are streams which represent an input source and an output destination. A stream can represent a variety of sources including, disk drives, databases, memory locations, text files etc. Therefore, a stream is a sequence of data to/from a program with the program interpreting each byte as a character according to the defined character code.

    I/O Stream

    I/O Stream
  • 2.3 Byte streams


    Bytes streams perform inputs and outputs of 8-bit bytes. Byte stream classes are descended from InputStream and OutputStream. FileInputStream and FileOutputStream are subclasses aimed at file I/O

    Typically a program will need to:
    import the correct packages
    Create a new stream
    Read bytes from the stream until the end of file is detected
    Interpret each byte as a character
    Close the stream

    Byte Stream Example:

    Stream Diagram
    Imports:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    

    New streams:

    in = new FileInputStream(filename);
    
    Read bytes and interpret as integers:

    while ((c = in.read()) != -1)
    

    Close stream:

    in.close();
    

  • 2.4 Character Streams


    Character streams are streams that are used to read and write character data to, and from, the stream. Byte streams only offer primitive I/O, therefore, it is better to use character streams if the program interprets bytes as characters. Different locales can use different character codes and internationalisation can be an issue. Typically Western locales use a superset of ASCII.

    FileReader and FileWriter are examples of stream classes are used with character streams. I/O which utilise stream classes automatically translate to and from the local character set. Therefore, programs that use character streams instead of byte streams are ready for internationalisation.

    Character Stream Example:

    try {
         inputStream  = new FileReader("xanadu.txt");
         outputStream  = new FileWriter("charoutagain.txt");
         int c;
         while ((c = inputStream .read()) != -1) {
                    outputStream .write(c);
         }
     } finally {
         if (inputStream  != null) { inputStream.close();  }
         if (outputStream  != null) { outputStream .close();  }
    }
    
  • 2.5 Line-Oriented I/O


    Ordinarily I/O occurs in larger units than single characters; one such example of this is a line of data. Typically a file is organised in such a fashion that each line represents a single record i.e. matches to a single object used by the programme. Lines are terminated using a combination of a carriage return (\r) and a line feed (\n).

    The stream classes used here are BufferedReader and PrintWriter. A buffered approach allows an area of memory to be loaded with data from the file which the program accesses; the buffer is reloaded once the program has emptied it.

    Line-Oriented I/O Example:

    inputStream  = new BufferedReader(new FileReader("xanadu.txt"));
    outputStream  = new PrintWriter(new FileWriter("lineoutagain.txt"));
    String l;
    while ((l = inputStream.readLine()) != null) {
        outputStream.println(l);
    }
    

    Note the FileReader object is wrapped using a BufferedReader object. You can also use buffers with output streams. A flush method can be used on a buffered output stream to force the buffer to be written to the output stream. Note the use of readLine and println to input and output a line at a time.
  • 2.6 Scanner


    Typically a line is not a single String but a collection of individual pieces of information which will make up the attributes of an object. A Scanner object is used to read characters from the input stream and construct tokens (In a Java program, all characters are grouped into symbols called tokens). Primitive data types are supported and by default whitespace (the space between characters) is used to separate groups of characters in the input stream, e.g blanks, tabs, line terminators. Where a token may legitimately include a white space character then an alternative delimiter can be specified. e.g. CSV, :, - etc.

    The hasNext() method can be used to query the stream to see if there are characters to be read. The next() method reads those characters up until the delimiter and constructs a String object. Variations on these methods can be used for integers, doubles etc. Again locale may be important as some locales use different punctuation symbols with numbers e.g., and.

    s = new Scanner(new BufferedReader(new FileReader(filename)));
    s.useDelimiter(Character.toString(delimiter));
    while (s.hasNext()) {
      if (s.hasNextInt()) {
          itemNumber = s.nextInt();
      }
         title = s.next();
         s.nextLine();
    }
    

    Note the use of the nextLine() method to clear the end of line characters.
  • 2.7 Formatting & Standard Streams


    Formatting & Standard Streams Diagram
    Similarly we may wish to format characters for output, which is typical with the Standard Output stream.

    PrintWriter stream prints formatted representations of objects to a text-output stream.

    print & println are used for single values
    format is used for more complex and precise outputs
    d formats an integer value
    f formats a floating point value
    n outputs a line terminator
    s used for strings
    c used for single characters

    Standard Streams:

    Standard Input:
    System.in

    Standard Output:
    System.out

    Standard Error:
    System.err

    Byte streams (previously covered)

    To use a character stream wrap using an InputStreamReader
    InputStreamReader cin = new InputStreamReader(System.in);
  • 2.8 Data Streams


    Data streams support binary I/O of primitive data type values such as boolean, char, byte, short, int, long, float, and double and String values. All data streams implement either the DataInput interface or the DataOutput interface; the most widely-used implementations of these interfaces are DataInputStream and DataOutputStream.

    Methods:

    Working with doubles: readDouble, writeDouble.

    Working with ints: readInt, writeInt.

    Working with Strings: readUTF, writeUTF.

    writeUTF method writes out String values in a modified form of UTF-8 (variable-width character encoding that only needs a single byte for common Western characters)

    Example:

    static final String dataFile = "invoicedata";
    static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };
    static final int[] units = { 12, 8, 13, 29, 50 };
    static final String[] descs = { "Java T-shirt",  "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain" };
    
    out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
    
    for (int i = 0; i < prices.length; i ++) {
         out.writeDouble(prices[i]);
         out.writeInt(units[i]);
         out.writeUTF(descs[i]);
    }
    
    in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
    double price;
    int unit;
    String desc;
    price = in.readDouble();
    unit = in.readInt();
    desc = in.readUTF();
    
  • 2.9 Object Streams


    Object Streams support I/O of objects. In order for a class to utilise object streams the class must implement the interface Serializable which allows and object to be represented as a sequence of bytes that includes the object's data and type. ObjectInputStream and ObjectOutputStream implement ObjectInput and ObjectOutput which are subinterfaces of DataInput and DataOuput respectively. An object stream can contain a mixture of primitive and object values. The readObject() and writeObject() methods allow us to read and write object to and from files, and throw the ClassNotFoundException.

    Example:

    PlaylistItem class must implement interface

     public class PlaylistItem implements Serializable {               
    

    Storing a playlist

    output = new ObjectOutputStream(new FileOutputStream("object.data"));
    for (PlaylistItem p : this.playlist) {        
         output.writeObject(p);
    }
    output.close();
    

    If Playlist class implements Serializable interface

    output = new ObjectOutputStream(new FileOutputStream("object.data"));
    output.writeObject(this);
    output.close();
    

    Read object

    FileInputStream fin = new FileInputStream(filename);
    ObjectInputStream ois = new ObjectInputStream(fin);
    PlaylistItem p;
    while ((p = (PlaylistItem) ois.readObject()) != null) {
          this.addItem(p);
    }
    ois.close();
    

    writeObject and readObject can become quite sophisticated, for example, An object can contain references to other objects. Consequently, writeObject can cause a large number of objects to be written.

    Object Stream Diagram

    readObject must reconstitute (read serialized objects back into RAM) object with all referenced objects reconstituted. A stream can only contain one copy of an object, though it can contain any number of references to it, therefore, it can write the reference more than once.