Chapter:Objects - Using - Exercises

From Juneday education
Jump to: navigation, search

Exercises on using objects

Let's look at some more classes from the Java API and what we can do with objects of those classes.

New material used in the exercises:

  • Properties class and Properties objects
  • System properties
  • The List interface, ArrayList class

Exercise 1. System properties

We'll start with a class called java.util.Properties which is a class describing objects whose purpose is to hold a list of settings in the form key-value. A Properties object can store information in a way that reminds us of a dictionary - if we want to store our favorite color, we can store it as a key-value pair in a Properties object, so that we later can look up "favorite_color" and get the color back. In the same Properties object we can store as many key-value pairs we want, as long as the keys are unique. So in the same Properties object we could also store our favorite programming language using for instance the key "favorite_programming_language". Later in the program, if we have access to the Properties object, we can retrieve both "favorite_color" and "favorite_programming_language".

But before we create our own Properties object, we'll actually ask the System class to give us a prefabricated Properties object with key-value pairs for information about our computer system! The System class offers a way to get such a Properties object via a call to System.getProperties(). Note that this call is not an instance method call! We don't have any instance of the System class (and the designers of the Java API have decided that we don't need to create one either!). We call the getProperties() method directly via the class name System, and store the reference to the Properties object we get back like this:

    Properties systemProps = System.getProperties();

After that we can use the Properties object with the system properties via the reference variable systemProps. However, there is one problem we have to fix first. The Properties class is located in the java.util package. So we have to import the class using an import statement, before we are allowed to use the class Properties.

You may remember that we didn't have to use any import statement when we were only using the String class and System class previously. You may also remember that both String and System were located in the package java.lang. The thing that was special with that package, was that we didn't need to import the path to that directory in order to use the classes in it. That was an exception to the rule that we always need to import classes that are in a different package than the class that needs to use the classes.

So, our program where we investigate the Properties object we get from System.getProperties() needs to have an import statement importing the java.util.Properties class, before the class declaration.

Let's look at the code you should write for the first program which uses the system properties. It will declare a reference variable of type "reference to Properties" and it will be assigned a reference to the Properties object we get from the System class. Then we'll ask the Properties object to list itself to the standard output stream (print itself to the terminal if you'd prefer that description) using the instance method list() and we'll give it the argument of System.out (which is an object that is connected to the stream that can output text to the terminal as we have seen when we've used System.out and the println() method).

The program should not belong to the package we used when we were playing with String objects. It should instead belong to theyorg.progund.props package. But we don't have the corresponding directory yet, so we must create that first. You should still be in the objects_in_java directory, as before. Issue the following command to create the directory:

$ mkdir -p org/progund/props

Remember, the org/progund directory is there from the previous exercises, so all we needed to do was to add the props directory inside the org/progund directory.

Your directory structure (if you are still in the objects_in_java directory) should now look like this:

.
`-- org
    `-- progund
        |-- props
        `-- strings

The source code file for the program should be named ListSystemProps.java and saved in the org/progund/props directory since the source code belongs to the org.progund.props package and declares a class called ListSystemProps.

This is the program code that you should write (finally!)

package org.progund.props;

import java.util.Properties;

public class ListSystemProps {

  public static void main(String[] args) {
    Properties systemProps = System.getProperties();
    systemProps.list(System.out);
  }

}

To compile and run (still from the same directory as before, objects_in_java) issue the following commands

$ javac org/progund/props/ListSystemProps.java && java org.progund.props.ListSystemProps

Note that a lot of system properties are being printed! Let's use the filter command grep to only display the properties that have to do with user settings:

$ javac org/progund/props/ListSystemProps.java && java org.progund.props.ListSystemProps | grep user

Note the | grep user at the end of the command line. The grep filter is included in your environment (GNU/Linux, Mac OS, or, Cygwin) and has nothing to do with Java programming. But we thought it would be interesting for you to see it in action! It is very useful to filter out interesting parts from a large amount of text.

What is the value of your property with the key "user.name"?

What is the value of your property with the key "user.language"?

What is the value of your property with the key "user.home"?

Suggested solution to Exercise 1. System properties explained

Expand using link to the right to see the full content.

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.props;
 2 
 3 import java.util.Properties;
 4 
 5 public class ListSystemProps {
 6 
 7   public static void main(String[] args) {
 8     Properties systemProps = System.getProperties();
 9     systemProps.list(System.out);
10   }
11 
12 }

What we see on line 8 Properties systemProps = System.getProperties(); is another way to get hold of an object. Here, instead of calling a constructor using the new operator, we ask a class to give us a reference to some object. In this particular case, we use the System class (in the java.lang package), to give us a reference to its properties objects. This is done by calling a method that belongs to the System class itself (we will later in the book see that such methods are called "class methods" or "static methods"). The call System.getProperties() can be explained like this: "Go to the System class, and call the method getProperties() there". That method returns a reference to a Properties object which is populated with key-value pairs for your system properties. We use the call to assign the reference to our variable systemProps (which is of type "reference to Propterties").

The whole statement Properties systemProps = System.getProperties(); can now be explained using the following words:

"Declare a Properties reference variable called systemProps and assign to it, the reference we get from calling System.getProperties()"

What our main method does here, is to create a reference variable called systemProps. But rather than creating a new object using new Properties(), we instead assign it a reference to an object that already exists (or is created by) the class java.lang.System.

On line 9 we have systemProps.list(System.out); which uses our local reference variable systemProps to call the method list on the object it refers to. The method list accepts an argument of where to list all the properties key-value pairs. We pass along a reference to the System.out object, which we have seen is connected to the standard out stream (the one we have used to print stuff to the terminal using println()).

So, our variable systemProps refers to an object of class properties. If we want that object to print its contents to our terminal, we send it the message list and we send along a reference to a place to print to. We could also say that we call the instance method list of the object and give the argument of where to print to.

From these two lines of code we learned two things: In a program we are writing ourselves, we can get a reference to an object some other class has or knows how to create. In our case, the System class offered the getProperties() method to programmers who wants a reference to an object that has all the system properties key-value pairs.

We also learned that we can now use our reference variable to send messages (or call instance methods) to an object. In our case, we sent the message list and we provided an argument telling the object what it could use to list its contents (in our case we sent along a reference to the System.out object, which is connected to our terminal).

Finally, some words on the import statement. We start our program source code with a package declaration which is only to give the compiler and JVM information on the location of our class and source code. Next we added an import statement saying import java.util.Properties;. This import statement was necessary, because we are using a class outside our own package, and outside the automatically imported java.lang package. The class Properties, as you can see from the import statement, is part of the java.util package. If we want to use the short name of the class (simply Properties) in our program, and we do want that!, we have to tell the compiler and runtime where to look for the Properties class!

If take the compiler as an example, without the import statement, it would get stuck while analyzing our code when it finds the Properties type on line 8. The compiler would look in the same directory as our file but wouldn't find any Properties class there. It would also look in the java.lang package (which is always imported automatically) but it wouldn't find it there either. The compiler would now give up and give as an error message about not finding the class Properties.

But with the import statement, the compiler would know where to look for the Properties class, since it says explicitly in the import statement that we will be using java.util.Properties. So the compiler has a path to the class Properties that it will use and the problem is solved!

Exercise 2. Accessing individual property values

In the next program (in the same package!) we'll see how to get the values of a certain property if we know the key. The class should be called GettingValues and belong to the org.progund.props package. What must we name the file, and where must we save the file?

If you answered "The file must be named GettingValues.java and be saved in the org/progund/props directory", then you are really starting to learn the use of packages! If you answered something else, please review the previous exercises.

Here's the code for the GettingValues class:

package org.progund.props;

import java.util.Properties;

public class GettingValues {

  public static void main(String[] args) {
    Properties systemProps = System.getProperties();
    String javaVersion = systemProps.getProperty("java.version");
    String operatingSystem = systemProps.getProperty("os.name");

    System.out.print("I'm running Java " + javaVersion);
    System.out.println(" on a " + operatingSystem + " machine.");
  }
}

You can look at the Solutions section below for some comments of what was going on in the previous two programs.

Suggested solution to Exercise 2. Accessing individual property values explained

Expand using link to the right to see the full content.

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.props;
 2 
 3 import java.util.Properties;
 4 
 5 public class GettingValues {
 6 
 7   public static void main(String[] args) {
 8     Properties systemProps = System.getProperties();
 9     String javaVersion = systemProps.getProperty("java.version");
10     String operatingSystem = systemProps.getProperty("os.name");
11 
12     System.out.print("I'm running Java " + javaVersion);
13     System.out.println(" on a " + operatingSystem + " machine.");
14   }
15 }

In this program, we use the same technique as before to get the system properties from the System class. But instead of listing all the properties to the terminal, we are only interested in a few values. The values we are interested in are stored in the Properties object, that we got a reference to from System, using the following keys: "java.version" and "os.name".

A Properties object, as said before, stores key-value pairs. If we want to get a value, we ask for it using the key. So, if we want to get the value representing the Java version we are using on our system, we use the key "java.version". If we want the name of the operating system (according to Java), we use the key "os.name". but how do we use a key to get a value?

Look at lines 8 and 9. On line 8 we declare a String reference variable called javaVersion. We could have called it anything we like but we thought javaVersion was a good and descriptive name for a String with the java version as the text value. The statement on line 8 both declares this String reference variable and assigns to it the result of sending a message to the object referred to by systemProps.

The message (the instance method) we use is getProperty and the argument we send along is the key "java.version" as a String literal. So the instance method call is getProperty("java.version"). But how do we tell Java which Property object we want to send this message? Again, we use the dot separator. We write the name of our reference variable systemProps and a dot and then the instance method and arguments. You can think of this as meaning:

Go to the object referred to by systemProps and call the instance method getProperty on that object. Send along the argument of the key for the value we are interested in.

The same principle is applied on line 9.

Exercise 3. A custom Properties object

Next, we'll create our own Properties object and populate it with some keys and values. We'll also use the system properties object to get some values and add them to our own Properties object.

The fully qualified name of the next program should be org.progund.props.CustomProperties (so, as usual, we need to save the source file accordingly in org/progund/props/CustomProperties.java

Here's the code for the CustomProperties class:

package org.progund.props;

import java.util.Properties;

public class CustomProperties {

  public static void main(String[] args) {
    Properties favorites = new Properties();
    favorites.setProperty("favorite.color", "Pink");
    favorites.setProperty("favorite.programming_language", "Java");

    Properties systemProps = System.getProperties();
    favorites.setProperty("favorite.encoding", systemProps.getProperty("file.encoding"));
    System.out.println("These are a few of my favorite things:");
    favorites.list(System.out);
  }
}

To compile and run this program, use the following:

$ javac org/progund/props/CustomProperties.java && java org.progund.props.CustomProperties

What favorite file encoding was listed? (A file encoding is a way to code text files on the hard drive - the encoding listed comes from the system property file.encoding which is the default encoding for text files on your system)

Please see the solutions section below for comments on how the code works.

Suggested solution to Exercise 3. A custom Properties object explained

Expand using link to the right to see the full content.

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.props;
 2 
 3 import java.util.Properties;
 4 
 5 public class CustomProperties {
 6 
 7   public static void main(String[] args) {
 8     Properties favorites = new Properties();
 9     favorites.setProperty("favorite.color", "Pink");
10     favorites.setProperty("favorite.programming_language", "Java");
11 
12     Properties systemProps = System.getProperties();
13     favorites.setProperty("favorite.encoding", systemProps.getProperty("file.encoding"));
14     System.out.println("These are a few of my favorite things:");
15     favorites.list(System.out);
16   }
17 }

Here we create our own Properties object using the normal way of calling a constructor for the object using the new operator keyword. The constructor we are using takes no arguments, hence the empty parentheses.

With a brand new, empty, Properties object, we can start storing our own key-value pairs. In this program we show you how to use the setProperty method to add a new key-value pair to a Properties object. Since a Properties object hold a collection of key-value pairs, adding a new entry must mean that we add both the key and the value to be stored. This is why the setProperty method takes two arguments! The first argument is the key, and the second argument is the value. In a Properties object, both the key and the value in each entry are references to Strings. So, when we add an entry for "favorite color" to our Properties object, we call the setProperty instance method using our variable favorites like this (as seen on line 8):

8     favorites.setProperty("favorite.color", "Pink");

The Properties object favorites refers to now has a new entry consisting of the key favorite.color and the value Pink. If we'd later wanted to retrieve the value for the property favorite.color, do you remember how to do that? The answer is to use the getProperty method like this: String favoriteColor = favorites.getProperty("favorite.color"); (and at the same time save the value using the favoriteColor String reference created in the same statement!).

Line 9 adds another key-value pair to the Properties object referred to by favorites. The really interesting thing in this program happens on line 12:

12     favorites.setProperty("favorite.encoding", systemProps.getProperty("file.encoding"));

As before, we use the favorites reference variable to add a key-value pair to the object referred to by favorites. But look closely at the second argument: systemProps.getProperty("file.encoding") which uses a method call as the value for the argument! As you can see, arguments are listed separated by a comma. In the simple example of adding the key "favorite.color" with the value "Pink", we simply listed two String literals separated by comma. Both arguments were evaluated to the reference address of the two String objects created by the literals. But on line 12, the second argument is not a String literal. It is a call to the getProperty method of the Properties object referred to by systemProps (created on line 11). The result of calling getProperty, as we've seen before, was a reference to a String representing the value associated with the key given as argument. This means that we can use this method call as an argument, where a String reference is expected!

From this we learn that expressions also can consist of method calls!

We could have broken up the statement on line 12 to several lines to make it more readable:

    String fileEncoding = systemProps.getProperty("file.encoding");
    favorites.setProperty("favorite.encoding", fileEncoding);

In the two lines above, we are first creating a String reference called fileEncoding and assigning it a reference to the value String we get from asking the systemProps object for the value stored with the key "file.encoding". On the next line, we use this String reference variable (fileEncoding) as the second argument when we store it using our favorites Properties reference.

So, once again, instead of first getting the "file.encoding" value and storing it using a String variable with the value from the getProperty method call, we can use the method call directly as an argument! This works because method calls can have a value and be used as expressions.

An argument that must be of type "reference to String" can take many forms. It can be a String literal such as "file.encoding", it can be a String reference variable like fileEncoding or it can be a method call to a method which returns a String reference such as in systemProps.getProperty("file.encoding")

On the last line of the main method, we ask our favorites Properties object to list itself on the standard output stream (our terminal), by sending it the message list(). How do we know that we sent that message to our specific object? We used the reference variable favorites and the dot separator: favorites.list(System.out); .

Exercise 4. Using ArrayList

Let's leave the Properties class for now and instead look at a class for creating objects representing lists. Also in the java.util package is a class called ArrayList. We've seen how to use arrays of String references, when we used the args variable with the arguments to the main method, previously. Arrays are useful, but they are a bit limited from an object oriented perspective. Arrays have no instance methods, and the only things we an do with arrays is to query the length variable for the number of elements in the array, and access the various elements using the index number with the [ ] square brackets separator. But more importantly, what we didn't say before was that arrays are fixed in size. Once an array is created, there is no way to change the size (number of possible elements). This quickly becomes a problem if we dynamically (during the course of the execution of the program) want to add elements, and we reach the maximum number of elements.

The class ArrayList comes to our rescue! An ArrayList object acts pretty much like a normal array, but it is growing dynamically. There is no maximum fixed size, so we can keep adding elements, without worrying that we reach the maximum size (unless the memory of the virtual machine or physical system runs out).

ArrayList objects also have instance methods, as all decent objects have. We can ask the object about its state, and we can ask it to remove all elements, query the object of what index a certain element has and more.

When we create an ArrayList, we need to provide the name of the class of the elements to be stored in the list. This is to help us getting type safety, so that we don't add elements of incompatible types by mistake. The compiler will help us ensure that an ArrayList for a certain class of elements only gets populated with references to objects of the correct type.

This is the syntax for declaring and creating an ArrayList for storing String references:

ArrayList<String> nameList = new ArrayList<String>();

Alternatively, you may use the slightly shorter form:

ArrayList<String> nameList = new ArrayList<>();

The empty "diamond" <> on the right-hand side works, because the compiler can figure out that you mean to create an ArrayList for String references, because you explicitly said so on the left-hand side.

Let's create a program which creates an ArrayList<String> and uses some of the instance methods on the object. We don't want the class for this program to belong to any of the packages we've declared so far, so we need a new directory for the package. Let's call the directory lists and create it like this:

$ mkdir org/progund/lists

So, the package we'll use will be called org.progund.lists

We'll call the program NameList.java

Here's the code for the program:

package org.progund.lists;

import java.util.ArrayList;

public class NameList {

  public static void main(String[] args) {
    ArrayList<String> nameList = new ArrayList<String>();
    nameList.add("Adam");
    nameList.add("Ana");
    nameList.add("Bart");
    nameList.add("Lisa");

    System.out.println("We now have " + nameList.size() + " names:");
    System.out.println(nameList);

    if ( nameList.contains("Bart") ) {
      System.out.println("The index of Bart is " + nameList.indexOf("Bart"));
    }

    nameList.remove("Bart");
    System.out.println("We now have " + nameList.size() + " names");

    if ( nameList.contains("Bart") ) {
      System.out.println("Bart is still there!");
    } else {
      System.out.println("There is no Bart in the list any more.");
    }

    System.out.println("Let's clear the list!");
    nameList.clear();
    System.out.println("We now have " + nameList.size() + " names");
    if ( nameList.isEmpty() ) {
      System.out.println("The list is now empty.");
    }
  }
}

Please see the solution section for comments on how the program works. You don't need to understand everything yet, this is just an exercise to demonstrate how you use a reference variable to call instance methods on a particular object! We'll write our own classes later in the class, and then we'll write our own instance variables and instance methods as well. Things will be clearer then. The idea here is to show you first how to use objects and methods, and only later how to write your own classes with methods and the whole shebang.

Suggested solution to Exercise 4. Using ArrayList explained

Expand using link to the right to see the full content.

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.lists;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class NameList {
 6 
 7   public static void main(String[] args) {
 8     ArrayList<String> nameList = new ArrayList<String>();
 9     nameList.add("Adam");
10     nameList.add("Ana");
11     nameList.add("Bart");
12     nameList.add("Lisa");
13 
14     System.out.println("We now have " + nameList.size() + " names:");
15     System.out.println(nameList);
16 
17     if ( nameList.contains("Bart") ) {
18       System.out.println("The index of Bart is " + nameList.indexOf("Bart"));
19     }
20 
21     nameList.remove("Bart");
22     System.out.println("We now have " + nameList.size() + " names");
23 
24     if ( nameList.contains("Bart") ) {
25       System.out.println("Bart is still there!");
26     } else {
27       System.out.println("There is no Bart in the list any more.");
28     }
29 
30     System.out.println("Let's clear the list!");
31     nameList.clear();
32     System.out.println("We now have " + nameList.size() + " names");
33     if ( nameList.isEmpty() ) {
34       System.out.println("The list is now empty.");
35     }
36   }
37 }

On line 3, we have an import statement where we import the location of the ArrayList class. We are creating and using an ArrayList in this small program, so as usual we must have the include statement for that class, since it is not part of the java.lang</package>. Remember that only classes from the <code>java.lang package are imported automatically ("imported" means that the compiler and runtime system knows where to find them) from the Java API.

On line 7 we have:

7     ArrayList<String> nameList = new ArrayList<String>();

This is a standard declaration and initialization of a reference variable. We declare the variable nameList as being of type "reference to ArrayList", and we initialize this variable to refer to a newly created ArrayList. The new syntax in this code is the <String> part. As we said before, this is there to specify what kind of things we want to be able to store in the ArrayList. By specifying String, we will now only be able to store String references in the ArrayList referred to by nameList. This special syntax is called "generics". Generics was added to Java in version 5, and is there to provide some help for us programmers so that we don't do stupid things. By specifying what types can be stored in an ArrayList, we get help from the compiler javac to check that we don't happen to store references of other types in the list. It also lets us get elements from the list in a convenient way. If we only can store String references in an ArrayList declared as above, we know that we can get an element from the list and be sure that is is a String reference.

Prior to Java 5 and the introduction to Generics, we could store basically anything in an ArrayList but whenever we wanted to get an element from the list, we hade to cast (convert) it to the correct type. Thanks to Generics, we don't have to fiddle with type casts when getting elements from such a list anymore.

The lines 8-11 adds elements to the ArrayList:

 8     nameList.add("Adam");
 9     nameList.add("Ana");
10     nameList.add("Bart");
11     nameList.add("Lisa");

This is, as you see, done via the reference nameList and calls to the instance method add(). This is one of the cases where the compiler would help us if we made a mistake! If we had tried to add something other than a String reference to the list, the compiler wouldn't have let us do so, and would have complained about it.

Note that perhaps surprisingly, we say String references when we talk about what is stored in the ArrayList. Remember that the double quote separators, creates new String objects and have the value of the reference addresses of those objects (pretty much as if we'd called constructors instead).

When the program is running, the Java virtual machine (JVM) will create the String objects, and store only the references in the array. When we want to use one of the objects in the ArrayList, we use the reference to the object to use it. There is no way to access objects directly in Java. Objects are always accessed via references.

On line 13 we have

13     System.out.println("We now have " + nameList.size() + " names:");

Here we create and print some facts about our ArrayList object. We use the + concatenation operator to create the full text. The interesting part is in the middle, where we use our reference nameList to ask the object for its size (the number of elements currently in the list).

The perhaps most surprising part of this program comes on line 14:

14     System.out.println(nameList);

Here we call the println method on the out object in the class System as usual when we want to print something to the terminal. But this time, we don't give a String reference as an argument, but a reference to an ArrayList! And the thing which gets printed is:

[Adam, Ana, Bart, Lisa]

The thing which is printed is a nicely formatted list of elements in order, of the ArrayList. The surprising part, is that it seems like println knew how to convert the ArrayList to this nice thing. Part of the truth (we'll learn more details about this later in the book!) is that it is actually the ArrayList object which knows how to convert itself to a String. There is a method in every class which is called toString() where code for creating a nice String representation of the state of the object can be written. ArrayList has appropriate code inside its version of toString.

You should know, however, that when you later create your own classes, if you want to offer a nice version of toString(), you have to write code for that yourself.

What perhaps is less surprising is that the list is printed with the elements in the same order as they were inserted (added) to the list. This is so because each subsequent call to add() for a list, adds the new element at the end of the list.

On lines 16-18 we have:

16     if ( nameList.contains("Bart") ) {
17       System.out.println("The index of Bart is " + nameList.indexOf("Bart"));
18     }

Here we demonstrate the call of an instance method which returns a value of type boolean (true or false). Such a method call fits very well as the condition of an if-statement! If the method returns a boolean value, it can be used as an expression of boolean type. And the if-statement requires such a boolean expression in its test (between the parentheses). The method is called contains() and checks if some element exists in the list. Not if the exact same object is present in the list, but an object which is "equal" to the argument of contains(). For Strings, equality tests are easy to understand. Two Strings are considered equal if they contain exactly the same characters in the same order. On the lines above, we tested if there was any String in the list, whose characters were equal to "Bart" (and there was!).

Since the contains() method call returned true (gave us the answer true), the execution, when you ran it, entered the if block and it executed the statement System.out.println("The index of Bart is " + nameList.indexOf("Bart"));.

The meaning of that statement is that we create a text for our old friend println() by concatenating a String with the call to indexOf("Bart"). The result was:

The index of Bart is 2

So, if we want to get the position (index, indices start at 0 as with normal arrays) of an element, we can use a reference to the ArrayList object and send the message (call the instance method) indexOf() and provide a reference to some object to look for. If the ArrayList object can find the element, it returns the index as an int. But how does it find an element? The ArrayList actually will have to loop through all its elements and compare them with the object given as argument (the object to search for) again using the equals() method. So again, the object we search for, doesn't have to be exactly the same object whose reference is in the list. It is enough that an object reference leads to an object which is "equal" to that of the argument.

What if there are more than one object which is equal to the one we want the index of? Then we get the index of the first match using equals.

There is a method in ArrayList called lastIndexOf() which works in a similar way, but instead returns the greatest index (if it finds a match). Both methods returns -1 if no match is found.

On lines 20-21 we find:

20     nameList.remove("Bart");
21     System.out.println("We now have " + nameList.size() + " names");

First the element which matches "Bart" is removed from the list. Next we again print the size of the list. The result is, not surprising, that the list is now one element smaller than before!

On lines 23-27 we demonstrate an if-else statement, which checks if there is any element matching "Bart" in the list. If there is, a text indicating this is printed, otherwise a text telling us that there was no "Bart" in the list is printed:

23     if ( nameList.contains("Bart") ) {
24       System.out.println("Bart is still there!");
25     } else {
26       System.out.println("There is no Bart in the list any more.");
27     }

Since we only had one "Bart" String in the list, and it was removed, the latter message was printed.

After this, we clear the list. Calling clear() on an ArrayList via a reference removes all elements and the list is empty after that:

29     System.out.println("Let's clear the list!");
30     nameList.clear();
31     System.out.println("We now have " + nameList.size() + " names");

The last thing which happens in the program is that we show how you can use a reference to an ArrayList to ask the list if it is indeed empty. We use the call of isEmpty() as the boolean expression in an if-statement:

32     if ( nameList.isEmpty() ) {
33       System.out.println("The list is now empty.");
34     }

Method which checks some facts and returns a boolean (true or false) often have names like this. It makes the code easy to read and understand. The claim we want to test for validity is that the list "is empty". Giving the method the name isEmpty makes reading the code almost plain English: "If nameList is Empty...". This is a good naming scheme for methods which produce a boolean response.

Exercise 5. ArrayList challenge exercise

Write a class in the org.progund.lists package called LoopChallenge with a main method which does the following:

  1. Check if there were any arguments to the main method (given on the command line)
    1. If there were no arguments, write the following error message to the terminal "You must provide an argument".
    2. Exit the program (You can use the following code for that: System.exit(1); )
  2. If there were arguments, create an ArrayList and add every argument to the list.
  3. Write a message to the terminal indicating how many arguments were given.
  4. Loop through the list and print every element to the terminal
  5. Write a message saying "Here they come in reverse order"
  6. Loop through the list in reverse order and print every element to the terminal

In order to loop through an ArrayList in reverse order, you may use the following strategy:

Use a for-loop starting the loop variable i at list.size()-1 and loop as long as the loop variable is >=0. Use i-- as the change of the loop variable.

To access an element at a given index, use list.get(i) where list is the reference to the list, and i is the index you want to access.

If you have forgotten how to pass arguments to a program, check out Sending information to the main method.

Suggested solutions to Exercise 5. ArrayList challenge

Expand using link to the right to see the full content.

Version 1, simplest solution?

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.lists;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class LoopChallenge {
 6 
 7   private final static int NO_ARGUMENTS=1;
 8 
 9   public static void main(String[] args) {
10     if (args.length == 0) {
11       System.err.println("You must provide at least one argument.");
12       System.exit(NO_ARGUMENTS);
13     }
14 
15     ArrayList<String> argList = new ArrayList<>();
16     for (String arg : args) {
17       argList.add(arg);
18     }
19 
20     System.out.println("You gave " + args.length + " argument(s)");
21 
22     for (String arg : argList) {
23       System.out.println(arg);
24     }
25 
26     System.out.println("Here they are in reverse order:");
27 
28     for (int i = argList.size()-1; i >= 0; i--) {
29       System.out.println(argList.get(i));
30     }
31   }
32 }

Comments on the solution above: On line 7 we declare a class variable to hold the argument to System.exit(). System.exit aborts the program and sends a return value to the operating system. Any value other than 0 means that the program failed somehow. We could have written System.exit(1) but if we have many error conditions represented by numbers, it would be hard to remember what kind of error they represented. Better to use a constant value declared as on line 7, rather than hard coded magic numbers that no one will remember what they stood for.

The statement private final static int NO_ARGUMENTS=1; declares an internal variable for the class, whose value cannot be changed. private means that this is an internal affair of the class (no code from other classes may use this variable), final means that this variable cannot have its value changed, static means "belongs to the class and not to any particular object of the class". Constants like this usually have names with only capital letters and underscore to divide parts of the name.

Lines 15-17 uses a "for-each loop" to add all elements from the args array to the ArrayList object:

15     for (String arg : args) {
16       argList.add(arg);
17     }

Lines 27-29 loops through the ArrayList in reverse order, starting at the last index all the way down to index 0:

27     for (int i = argList.size()-1; i >= 0; i--) {
28       System.out.println(argList.get(i));
29     }

Explanation of the for-loop: int i=argList.size()-1 initializes the loop variable i to the last index. Remember, since indices start at 0, the highest index is always the same as the size of the list minus one. i>=0 is the condition of the for-loop. We are going down but we should stop at the first index, namely 0. i-- is the change part of the for-loop. We want to decrease i after each iteration, and you can write like this to decrease an integer with one. It is the same as the expression statement i=i-1; but no one uses that, since i-- is so much shorter...

In the body of the loop we have: System.out.println(argList.get(i));. The interesting part here is of course argList.get(i) which is a simple call to the instance variable get() in the ArrayList class. The method takes one argument, the index for which element we want to access.

Version 2, harder but less code:

Download this file here if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.lists;
 2 
 3 import java.util.List;
 4 import java.util.Arrays;
 5 import java.util.Collections;
 6 
 7 public class LoopChallengeVersion2 {
 8 
 9   private final static int NO_ARGUMENTS=1;
10 
11   public static void main(String[] args) {
12     if (args.length == 0) {
13       System.err.println("You must provide at least one argument.");
14       System.exit(NO_ARGUMENTS);
15     }
16 
17     List<String> argList = Arrays.asList(args);
18 
19     System.out.println("You gave " + args.length + " argument(s)");
20 
21     for (String arg : argList) {
22       System.out.println(arg);
23     }
24 
25     System.out.println("Here they are in reverse order:");
26 
27     Collections.reverse(argList);
28 
29     for (String arg : argList) {
30       System.out.println(arg);
31     }
32   }
33 }

First of all, we don't use ArrayList directly in this version. Instead we use something called an interface, and the interface is called List. That interface describes what we can do with different kinds of list (for instance ArrayList objects). We'll talk a lot more about interfaces in coming chapters, so don't worry if you don't follow us completely. We also use some utility classes from the java.util package. Together, we are using two classes and one interface from that package so we import it like this:

3 import java.util.List;
4 import java.util.Arrays;
5 import java.util.Collections;

Our list reference variable is in this version not explicitly an ArrayList, but rather has the more general type List. This is useful, because we can now use a utility class method from the Arrays class to convert the array args to a List like this (on line 16):

16     List<String> argList = Arrays.asList(args);

The Arrays class has a lot of useful class methods (methods we don't need object references to use, we call them directly using the class name!) for manipulating arrays. This particular message creates an object of interface type List (for instance an ArrayList object) from an array. This way, we don't have to iterate through the array and add each element to our list object.

Once again, you don't have to understand all this yet! We are showing it just as a teaser for what comes in next chapters. We want to show you code that contains stuff you don't have seen yet, and our hope is that you will at least recognize parts from this code when we introduce more stuff in later chapters.

On line 26, we use another useful utility class from the java.util package, Collections. There are many types of collections but List is one type of collection. The Collections utility class contains many useful class methods for manipulating various kinds of collection objects. Here, we use the class method reverse(), which takes a list and reverses the order of all the elements! This allows us to use a standard for-each loop again to print the elements in reversed order:

26     Collections.reverse(argList);
27 
28     for (String arg : argList) {
29       System.out.println(arg);
30     }

Note that the list is now still reversed, and if we wanted to use it in the original order, we'd have to reverse it again before that use.

The Object Oriented suggested solution consists of two files. It is here just as a reference for you to read and try to understand. It is a great reading exercise to prepare you for the next chapters to come! You see, here we create our own class to do the dirty work, which allows us to keep the main method clean and short, and quite easy to read! Enjoy the reading!

File: org/progund/lists/LoopChallengeOOVersion.java:

Download this files here (LoopChallengeOOVersion with the main method) and here (ArgumentHelper class, used in the main of the LoopChallengeOOVersion class if you want to look at it. Remember that if you want to compile and run it, it must be in the correct directory structure!

 1 package org.progund.lists;
 2 
 3 public class LoopChallengeOOVersion {
 4 
 5   public static void main(String[] args) {
 6     ArgumentHelper argHelper = new ArgumentHelper(args);
 7 
 8     if (argHelper.hasArguments()) {
 9       argHelper.start();
10     } else {
11       System.err.println(argHelper.errorMessage());
12     }
13     System.exit(argHelper.exitCode());
14   }
15 }
Class diagram for the ArgumentHelper class

File: org/progund/lists/ArgumentHelper.java:

 1 package org.progund.lists;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class ArgumentHelper {
 6 
 7   private static final int NO_ARGUMENTS = 1;
 8 
 9   private String[] args;
10   private ArrayList<String> argList;
11 
12   public ArgumentHelper(String[] args) {
13     this.args = args;
14     init();
15   }
16 
17   private void init() {
18     if ( hasArguments() ) {
19       argList = new ArrayList<String>();
20       for (String arg : args) {
21         argList.add(arg);
22       }
23     }
24   }
25 
26   public boolean hasArguments() {
27     return args.length != 0;
28   }
29 
30   public void start() {
31     if ( hasArguments() ) {
32       System.out.println("You gave " + argList.size() + " arguments");
33       printArgs();
34       printReverse();
35     }
36   }
37 
38   private void printArgs() {
39     System.out.println("These were the arguments:");
40     for (String arg : argList) {
41       System.out.println(arg);
42     }
43   }
44 
45   private void printReverse() {
46     System.out.println("Here they are in reverse order:");
47     for (int i = argList.size()-1; i >= 0; i--) {
48       System.out.println(argList.get(i));
49     }
50   }
51 
52   public int exitCode() {
53     if ( hasArguments() ) {
54       return 0;
55     } else {
56       return NO_ARGUMENTS;
57     }
58   }
59 
60   public String errorMessage() {
61     if ( !hasArguments() ) {
62       return "You must provide at least one argument!";
63     }
64     return "";
65   }
66 }

Links

Further reading

Where to go next

Next chapter is: Objects_-_Java_API_documentation

« PreviousBook TOCNext »