User:Lstor

From Wikipedia, the free encyclopedia

Contents

[edit] From Python to Java

[edit] Why learn Java?

  • Java is faster.
  • Java is more suitable for larger systems, because it is a stricter language.
  • Which also makes it easier to debug.

[edit] Our first example: Hello, World!

Let's get right to the code. We'll start with a program that most languages are introduced with: The Hello World program.

Java

1: class Hello {
2:     public static void main(String[] args) {
3:         System.out.println("Hello, World!");
4:     }
5: }

Python

1: print "Hello, World!"

[edit] Code analysis

On line 1, we start the class definition of our entry point class, designed to hold our main() function. On line 2, we start the function definition of the main() function. This is where program execution starts when the program is run. The first two lines in this example needs to be present in practically every Java program we will write, but the contents of main() may change. This means that as long as we have the first two lines in place, we can write procedural code inside main, much like Python. On line 4, we output «Hello World!».


We'll immediately notice a couple of things different from Python:

  • There's a lot more code. This is a typical difference between Java and Python: In Python, the interpreter generally understands what

you want, whereas in Java, you have to be more specific.

  • We use curly braces ( { and } ) to open and close code blocks, and semicolon at the end of each statement. This is in correspondance with Java being a more strictly typed language.
  • We define a class even if we will just print a string. In Java, all the code is organized into classes, and interaction between classes plays a vital role in code design.
  • There are a lot of keywords. We will get back to these later: For now, you only need to know that they need to be there. Similarly, there are type declarations wherever there is a variable or functions involved: We will take a closer look at types shortly.


[edit] Use the source, Luke!

In order to run this example program, we need to compile it. To compile the program, we need a Java compiler. The compiler runs through the source code and converts it to Java bytecode, which can then be run on a Java Virtual Machine. Assuming there already is a compiler installed on the system you use, you will need to make sure that the CLASSPATH is set correctly. Note: on computers at the university, the classpath should already be set correctly. When the compiler is properly set up with a valid CLASSPATH, you give the following command at the console:

> javac HelloWorld.java

This will invoke the Java compiler on the HelloWorld.java source file. The output of the compile operation is one or more class files, each containing the compiled source for one specific class. For our HelloWorld example, the output will be a file called HelloWorld.class. To run this class file:

> java Hello

This will find the Hello class in any of the directories specified in the CLASSPATH, and run it through the Java Virtual Machine. Note that the class given to the java executable should be the one declaring the main() function, in case we have other classes.

These steps, known as the compilation cycle will have to be repeated each time we change something in the source code.

[edit] The advantages of compiled programs

Compiled programs:

  • run faster, because the execution does not have to wait for an interpreter.
  • can be run on platforms without any development tools, whereas interpreted programs need the interpreter to run.
  • are sometimes easier to debug, because of the compile-time error checking performed by the compiler.


[edit] Not my type

As we saw earlier, Java explicitly declare types. Unlike Python, which is dynamically typed, Java is statically typed. This means that in Python, an object may change its type, whereas in Java, an int is born an int, lives its entire life as an int, and dies (in programming, this is called going out of scope) as an int. How do we make sure a variable is an int? We declare it to be an int when we define the variable. We do that by writing the type identifier in front of the variable name. Java (and many of the other more powerful languages, like C++) is strongly typed to mitigate the chance of the programmer doing anything he doesn't intend to, like overwriting an earlier variable. So, what does this mean when we are coding? Let us look at an example:

Java

1:  class Types {
2:      public static void main(String[] args) {
3:          // Non-class built-in types:
4: 
5:          byte    b = 127;           //  8-bit signed integer
6:          short   s = 32767;         // 16-bit signed integer
7:          int     i = 2147483647;    // 32-bit signed integer
8:          long    l = 9223327036854775807L;
9:                                     // 64-bit signed integer
10:         float   e = 2.718281828f;  // 32-bit single-precision float
11:         double pi = 3.1415926535;  // 64-bit double-precision float
12:
13:         char    c = 'a';           // 16-bit Unicode character
14:         // char    c = "a";           ILLEGAL! c is a String
15:         boolean v = true;          // Boolean (true / false)
16:
17:
18:         // Class types:
19:         String str = "Hello";
20:     }
21: }

[edit] Code analysis

This program shows a summary of the built-in types, and also shows how to create them. As you see, you have to explicitly declare the type of each variable. Note on line 8 that an integer normally is interpreted to be an int -- to get a long, we explicitly declare it, by appending an L to the number. The same applies to floats: They are normally interpreted as doubles, and single-precision floats need to be declared with a trailing f. Also note, on line 13, that characters (chars) are declared using single quotes -- double quotes are interpreted as a String. This means that the statement on line 14 is illegal, because a char cannot hold a String.

[edit] A comment on comments

As you can see from this example, comments in Java are different from comments in Python. There are two different kinds of comments in Java:

  • //, often called C++-style comments, are similar to Python comments: Anything from the comment sign to the end of the line is a comment
  • /* and */, often called C-style comments, can span over multiple lines, but can not be nested. Anything between the opening and closing comment sign is a comment.


[edit] Flow control statements: Going with the flow

Flow control statements in Java are similar to those in Python, with a few exceptions. if, and while work exactly the same way, although with a slightly different syntax. The for statement in Java is somewhat different from that in Python, and Java also have two additional flow control statements. Let us start by looking at those we already know.

Java

1:  class FlowControl {
2:      public static void main(String[] args) {
3:
4:          int x = 5;
5:          int y = 6;
6:
7:          if (x < y) {
8:              System.out.println(y + " is greater than " + x + "!");
9:          } else {
10:             System.out.println(x + " is greater than " + y + "!");
11:         }
12:
13:         x = 0; // x was declared on line 4
14:
15:         while (x < 10) {
16:             System.out.println("X: " + x);
17:             x += 1;
18:         }  
19:     }
20: }

Python

1:  x = 5
2:  y = 6
3:  
4:  if x < y:
5:      print "%d is greater than %d!" % (x, y)
6:  else:
7:      print "%d is greater than %d!" % (y, x)
8:  
9:  x = 0
10: 
11: while x < 10:
12:     print "X: ", x
13:     x += 1

[edit] Code analysis

On lines 4 and 5 we declare x and y to be two integers, and initialize them to 5 and 6, respectively. On line 7, they are tested against eachother, and on lines 8 and 10, various actions are taken depending on the outcome of the test. On line 13, we reset x to 0. Note that we now do not need to declare x to be an int: That was done on line 4, and we are working on the same x. The while-loop starting on line 15 is fairly straightforward: While x is less than 10, print its value and increment.

[edit] The Java for-loop

In Python, for-loops are normally used to iterate over a list of items. They work much the same in Java, but in its most basic form, the for-loop iterates over a set of numbers, rather than a list. In Java, it consists of four parts, any of which are optional:

for (INITALIZATION; CONDITION; INCREMENTING STATEMENT) {
    STATEMENTS;
}

Typically, you initialize a variable in the first section, see if some condition is true in the second section, increment the variable in the third section and perform one or more actions in the fourth section. Note: A for-loop does not need to iterate over a set of numbers. Basically, what a for-loop does is: Execute the INITIALIZATION-statement once before entering the loop, execute the CONDITION-code each time through the loop and break out of the loop if it evaluates to false, and then execute the statements inside the code block. At the end of each pass through the loop, the INCREMENTING STATEMENT is performed. Taking advantage of this, the for-loop can become a very versatile tool, but we will not spend more time studying that here. An example of an ordinary for-loop is in its place:

Java

1:  class For {
2:      public static void main(String[] args) {
3:  
4:          for (int i = 0; i < 10; ++i) {
5:              System.out.println("I is: " + i);
6:          }
7:  
8:          // "Forever":
9:          for (;;) {
10:             System.out.println("This loop goes on and on and on and...");
11:         }
12:     }
13: }

Python

1: for i in range(10):
2:     print "I is:", i
3: 
4: while True:
5:     print "This loop goes on and on and on and..."

[edit] Code analysis

Most of the action is on line 4: First, i is declared to be an integer with value 0. For each pass through the loop, i is tested to see if it is less than 10: if it is, another iteration through the loop is made. During each pass, the current value of i is printed, on line 5. After each pass, the value of i is incremented by one, using the increment operator ++. We will examine ++ in more detail later.

As I mentioned, any part of the for-loop may be left out, and this is demonstrated on line 9. No initialization, condition testing or incrementation is performed, which means that the loop will never end. Writing an endless loop with an empty for is often called a forever-loop.

In the section regarding container constructs, we will look at another kind of for loop in Java, which is quite similar to the Python for loop.

[edit] The do...while-loop

The first of the non-Python flow control constructs we will take a look at is the do...while-loop. This is basically a while-loop which ensures at least one iteration through the loop. We go straight to the code:

Java

1:  class DoWhile {
2:      public static void main(String[] args) {
3: 
4:          int x = 0;
5:          do {
6:              System.out.println("X is " + x);
7:              x++;
8:          } while (x > 5);
9:
10:      }
11: }

[edit] Code analysis

The do...while starts on line 5. Anything inside the do { and }-brackets is performed at least once. Then, after it has been performed the first time, the while-condition is tested on line 8. At this point, x is 1, which obviously is not more than 5, and so execution stops.

[edit] The switch-statement

The second construct we do not know from Python is the switch. This is not a loop, but essentially a compound if-statement. The switch takes an expression that can be evaluated to an int, and tests its value against the specified cases. If it finds a match, the code from that case is then executed. This is best illustrated by code:

Java

1:  import easyIO.*;
2:   
3:  class Switch {
4:      public static void main(String[] args) {
5:          System.out.println("Enter a number: ");
6:          In i = new In();
7:          int x = i.inInt();
8:  
9:          switch (x) {
10:         case 1:
11:             System.out.println("X is one");
12:             break;
13:         case 2:
14:             System.out.println("X is two");
15:             break;
16:         case 3:
17:             System.out.println("X is three");
18:         default:
19:             System.out.println("X is neither one, two or three");
20:             break;
21:         }
22:     }
23: }

Python

1: x = int(raw_input("Enter a number: "))
2: if x == 1:
3:     print "X is one"
4: elif x == 2:
5:     print "X is two"
6: elif x == 3:
7:     print "X is three"
8: else:
9:     print "X is neither one, two or three"

[edit] Code analysis

To make the example a little more interesting, we read one int as input from the user. To do this, we must import the corresponding library, which is done on line 1. Lines 6 and 7 handle the actual input -- we will get back to input/output later. On line 9, the x is evaluated, and the appropriate case is chosen accordingly. If the user input is neither 1, 2 or 3, the default-branch is chosen instead. The perceptive reader will notice that for case 3, there is no trailing break-statement. What does this mean? We know break from Python, and we know that it causes execution of code to continue right after the flow control statement. In a switch, the code enters at the first possible match, and then exits once it encounters a break. If it never encounters a break, it will run through to the end of the switch. In other words, if the user enters 3 as input, both the case 3 and the default blocks will be executed! For this program, this is a (deliberate) bug, but this behaviour can be useful in certain cases.

[edit] Missing in Java

  • In Python, there are traces of a programming paradigm known as functional programming. In Java, these elements are absent. List comprehension is the most evident trace of functional programming that does not exist in Java.
  • The Python feature known as keyword arguments is not present in Java. You always have to specify arguments in the order they are defined in a function definition.
  • In Python, you can define special methods in a class, allowing you to overload arithmetic operators and perform similar operations. Such special methods, often called operator overloading, is not present in Java. This means that in order to e.g. add two user defined classes, you have to define a method to do this, and call it in the conventional way. Example:

Java

chain.add(link);

Python

chain += link

[edit] A word on style

In many programming languages, there exists holy wars regarding how to represent code. Should class names begin with capital letters? How should the code be indented? Where should we put the curly braces? Fortunately, there are official Java guidelines regarding code style. Some of these are:

  • Variable names should begin with a small letter, and each subsequent word should be capitalised. This is called camelCase. Example: someVariable
  • Class names should begin with a capital letter, and each subsequent word should be capitalised. Example: MyClass
  • Opening curly braces should be on the same line as the statement they are opening; closing curly braces should be indented at the same level as that statement. Everything between should be indented one extra level. Example:
 if (someThing) {
    // ...
}

A few other general rules:

  • A function should not be longer than one screen height (typically 25 lines).
  • One line of code should not be wider than 80 characters.
  • The code should have as few comments as possibly to be perfectly easy to understand, but no less.
  • Comments should, as a general rule, describe what the code does, not how the code achieves it.

[edit] Operators in Java

Operators in Java are much like operators in Python. However, Python uses lexical operators, i.e. and, or etc. wherever it makes sense, whereas Java uses symbols instead. The following table shows the differences between Java and Python:

Operators in Java Operators in Python
x && y x and y
x || y x or y
!x not x
pow(x, y) x**y
N/A (use indexing instead) in

All arithmetic operators not listed here, like +, -, +=, -= etc., are the same in the two languages.

[edit] The pre- and post-increment and -decrement operators

In the example with the for-loop, we looked at the increment-operator, which increments a variable by one. There are several versions of this operator:

  • The pre-increment-operator, ++x, which increments x by one and then returns it
  • The pre-decrement-operator, --x, which decrements x by one and then returns it
  • The post-increment-operator, x++, which returns the value currently held by x, and then increments it
  • The post-decrement-operator, x--, which returns the value currently held by x, and then decrements it

The following code illustrates this:

Java

int x = 42;
System.out.println("X plus plus: " + x++); // Print x (42), and then increment to 43
System.out.println("Plus plus X: " + ++x); // Increment x to 44, and then print


[edit] Input / Output

We have already seen how to output simple text, and in the switch example we got a preview of how to get input. Let us take a closer look at how input and output works.

[edit] Output

Output is achieved using the System.out.println(output) function call. This prints output to stdout, i.e. usually the screen/terminal, and appends a trailing newline. If we do not want the trailing newline, we would use System.out.print() instead. Also, if we want to write to stderr, we would use the System.err.println() and System.err.print() functions. What are the differences between stdout and stderr? stdout is the standard output stream, and we write to stdout whenever we want to output text under normal circumstances. stdout is buffered, which means that writing to the terminal window, or whatever stdout is connected to, is more efficient. However, in case something goes wrong, and we want to print an error message, we want to use stderr, which is the standard error output stream. stderr is not buffered, which means that the error message we write is written to the terminal right away. If stdout is used for outputting error messages, you risk that the text gets lost in the buffer in case of premature termination of the program.

[edit] Input

In order to read input from stdin (usually the keyboard), we have to create an object that is responsible for reading input. In order to read from a file, we only need to supply the URL to the file in question to the constructor of the In-object. Remember that Java is statically typed, so if we want to get an int from the user, we have to ask our input object to return an object of type int. The functions/types supported by our In object are:

Function call Type / functionality
inLine() Read a line
inChar() Read a character
inInt() Read an integer
inWord() Read a series of characters
inBoolean() Read a boolean
inByte() Read a byte
inDouble() Read a double
inFloat() Read a float
inLong() Read a long
inShort() Read a short
ready() return true if there is input to read
close() Close the file descriptor

This should be fairly straightforward, but let us take a look at a quick example.

Java

1:  import easyIO.*;
2:
3:  class InputOutput {
4:      public static void main(String[] args) {
5:          In i = new In();
6:  
7:          System.out.print("Enter a number: ");
8:          int x = i.inInt();
9:  
10:         if (x == 42) {
11:             System.out.println("Good number!");
12:         } else {
13:             System.err.println("Error: Bad number");
14:         }
15: 
16:         Out o = new Out("output.txt");
17:         o.outln("Beginning of file");
18:         o.out("Some number: ");
19:         o.outln(42);
20:         o.close();
21:     }
22: }

Python

1:  import sys
2:  
3:  x = int(raw_input("Enter a number: "))
4:  if x == 42:
5:      print "Good number"
6:  else:
7:      sys.stderr.write("Error: Bad number")
8:  
9:  o = file("output.txt", "w")
10: o.write("Beginning of file\n")
11: o.write("Some number: ")
12: o.write(str(42))
13: o.write("\n")
14: o.close()

[edit] Code analysis

On line 1, we import the easyIO library. This needs to be present whenever we want to use In or Out objects. We initialize our first In object on line 5 with a call to new. We will get back to new in the section regarding classes. Note that the function call after new is a call to In's constructor. This is equivalent to the call to file's constructor on line 9 of the Python code. However, since we do not specify a file name in the call to In's constructor, we read from stdin instead of a file. On line 8, we ask for a new int by calling In.inInt(). If we wanted a double or char instead, we would have replaced inInt() with the corresponding function from the table above.

An Out object is created on line 16, and is used on lines 17-19. Note the various calls to out() and outln() depending on whether or not we want a newline character. We could also manually output one, using "\n". On line 20, we close the file pointed to by the Out object.

[edit] Functions

It is time to learn about functions. In Java, all the code we write is encapsulated within a class. This means that we do not have any code that does not belong to a class, and functions are no exception from this rule. In other words, all functions in Java are member functions, or methods. However, functions local to a class, ie. functions that are member functions to this particular class, work almost like global functions (non-method functions) in Python.

A function in Java consists of a few more identifying keywords than in Python. The most notable difference is that Java functions must specify a return type, and the returned object must be of that type. The same goes for arguments: Their type must be specified. Let us take a quick look at a function definition in Java.

Java

void myFunction(int x, int y) {
    /* Code */
}

Python

def myFunction(x, y):
    # Code

This is a function called myFunction, which takes two arguments of type int, and returns void. void is another way of saying that the function does not return anything. void is therefore much like None in Python.

[edit] Access modifiers

Because of the encapsulation in Java, not all functions are globally visible. Some functions are designed to work "behind the curtains", and should not directly be used by the user of the class or function. Because of this, Java has what is called access modifiers. These specify who should see and be allowed to use a function. public means that everyone, both inside and outside of the class should have access to the function. In Python, all functions, methods and variables are public. The private keyword means that only members of the class are allowed to use a function or variable. In addition, there is another keyword that we will not be using in this course: protected means that only members of this class and subclasses of this class may see and use members and functions.


[edit] Classes

It's time for the core part of the Java language: Classes. Everything we write will be in a class, and knowing how classes work is crucial for writing good code. Classes in Java work much the same as classes in Python: They have constructors, they have member variables, they have methods, and so on.

Let us begin with a simple class:

Java

1:  class Car {
2:      private String brand;
3:      private int modelYear;
4:  
5:      // Constructor
6:      public Car(String brand, int modelYear) {
7:          this.brand = brand;
8:          this.modelYear = modelYear;        
9:      }
10:  
11:     public void print() {
12:         System.out.println("Car brand: " + brand);
13:         System.out.println("Model year: " + modelYear);
14:     }
15: }

Python

1: class Car:
2:     def __init__(self, brand, modelYear):
3:         self.brand = brand
4:         self.modelYear = modelYear
5: 
6:     def output(self):
7:         print "Car brand:", self.brand
8:         print "Model year:", self.modelYear

[edit] Code analysis

The code should be pretty straightforward, but there are a few things worth mentioning. First of all, note on lines 2 and 3 how member variables are explicitly declared in the class, and not in the constructor, rather than just showing up when we want to use it. This may seem like extra work, but it actually has several advantages. By explicitly declaring variables, we know which variables are members of the class, and what type they have. We know where to look for comments regarding their use, and most important: We know that they are there. A somewhat common error in Python is to have some kind of if-statement in the constructor that prevents some variable to be created, or failing to initialize a variable we need initialized, and then attempting to use the variable later in the class. With this explicit declaration of variables, we mitigate the chances of such errors.

Note that the constructor in Java is called the same as the class, and does not return any values. (public is an access modifier, as discussed earlier, and is not a return type). Also, note that in Java, self is called this, and unlike Python, we do not need to explicitly write this unless there is ambiguity. In the constructor, on lines 6-9, the two parameters passed have the same names as the member variables, and so we need to explicitly identify the member variables with this when we want to refer to them. In the print() function on lines 11-14 there are no other brands or modelYears, so we do not need to explicitly state this.

[edit] Creating and using classes -- are you 'new' to 'this'?

To use a class, we have to create an instance of the class -- an object to work on. This is achieved using the new keyword, followed by a call to one of the class' constructors. Once we have done this, we can use the new object as we wish.

Let us create a car:

Java

Car c = new Car("Stallion", 1504);

Python

c = Car("Stallion", 1504)


[edit] Function overloading

Unlike Python, Java does not allow you to specify default values of function parameters. Instead, there is a feature knows as function overloading, which allows us to achieve the same, as well as a few other handy tricks. Function overloading means that we create several versions of the same function, where the function signature of each version must differ. The function signature is the function name, as well as its types and number of arguments. The return value is not a part of the function signature. This means that we may have two functions with the same name, as long as they have different number and/or types of parameters. For instance, consider a class GuestList, which contains a list of guests at a given event. When working with such a GuestList, we may want to invite another guest, or we might invite another group of guests. This means that we will probably need at least two functions for adding guests: One to handle a single guest, and one to handle a group of guests. Let us have a look at an example of such a GuestList.

Java

1:  class GuestList {
2:      // Constructor
3:      public GuestList() { /* ... */ }
4:  
5:      public void add(Guest g) {
6:          // Add a single guest to the list
7:      }
8:  
9:      public void add(GuestList rhs) {
10:         // Add a group of guests to the list
11:         // (A group of guests is represented by another GuestList)
12:     }
13: }

[edit] Code analysis

Most of the code is pretty straightforward - or omitted. Note that we have two add() functions. The first version operates on a single guest, the second on a group of guests. (class Guest is irrelevant to the code we write in GuestList, and its definition is omitted). When GuestList.add(argument) is called, the compiler evaluates argument's type. If its type is Guest, the code for the first add() function is executed, if its type is GuestList the second add() method is executed, and if it is neither, an error will be raised.

Function overloading can, as mentioned, be used to simulate default parameters. Consider a class for representing a Rubik's Cube. A Rubik's Cube typically consists of 3x3x3 coloured squares. Sometimes you want to kick it up a notch and play with a 4x4x4 cube instead (often called Rubik's Revenge). Such a class would have a constructor which created a 3x3x3 cube by default, but could have the ability to create a 4x4x4 cube when asked to do so. The following code illustrates how this would be solved:

Java

1:  class RubiksCube {
2:      private int size;
3:
4:      // Default constructor
5:      public RubiksCube() {
6:          size = 3;
7:
8:          // Create a proper cube
9:      }
10:
11:     // Specialized constructor
12:     public RubiksCube(int size) {
13:         this.size = size;
14:
15:         // Create a proper cube
16:     }
16: }

[edit] Code analysis

The default constructor is defined on lines 5-9. It handles the usual case, when someone wishes to create an ordinary Rubik's Cube. Should someone want to create a larger or smaller cube, they pass the size to the constructor -- in that case, the second constructor is called.


[edit] Static methods and variables

In our declarations of main(), we have been using public static void main(). Both public and void has been explained, but we have not yet looked at what static means. A static method or variable is one that is defined for the class as a type, rather than objects of that type. In other words, we do not need a specific object of that class to call the method -- we can call the method directly. Technically, this is achieved by not passing the this argument to static methods. This implies that static methods may not access any non-static member variables.

Let us look at an example.

Java

1:  class Car {
2:      static private int counter;
3:  
4:      public Car(String brand, int yearModel) {
5:          ++counter;
6:      }
7:  
8:      public static int numCars() {
9:          return counter;
10:     }
11: }
12: 
13: class StaticExample {
14:     public static void main(String[] args) {
15:         System.out.println("Number of cars: " + Car.numCars());
16:         Car c1 = new Car("Audi", 2010);
17:         System.out.println("Number of cars: " + Car.numCars());
18:         Car c2 = new Car("Toyota", 1995);
19:         System.out.println("Number of cars: " + Car.numCars());
20:     }
21: }

[edit] Code analysis

The Car class is defined on lines 1 to 11. On line 2, we declare a static variable, to hold the global number of cars, i.e. the number of Car objects. In the constructor, on line 5, this variable is incremented each time a new Car is made. The function defined on lines 8 to 11 returns this number. Note the way this function gives access to a private variable. Such functions are common in Java, and other languages with access modifiers, and are called accessor functions. Their use is to ensure proper access to hidden variables.

The class on lines 13-20 is created in order to show an example run of the Car class. On lines 15, 17 and 19, we print the number of cars. Note that to print the number of cars, we call Car.numCars(); rather than c1.numCars() or c2.numCars() -- we call the method from the class rather than from an instance of the class. At first, we do not have any Cars, so 0 is printed. Then we create a Car, which means that the counter is incremented, so that when we print the number of Cars again, the number is now 1.


[edit] Container constructs

We often want to operate on a list or series of objects, rather than just single instances. In Python, there are several built-in constructs for performing these tasks: dictionaries, lists, tuples, etc. In Java, there is only one built-in feature -- arrays -- to help us achieve this, and arrays have several limitations. Fortunately there are also a few classes that come with the standard library. We'll take a look at a small selection of these shortly, but we will start with a closer look at arrays.

[edit] Arrays

Arrays are Java's built-in counterpart to Python's lists. However, there are some significant differences between lists and arrays:

  • Arrays are homogeneous, meaning that they can only contain objects of one type.
  • Arrays have a static size. If you want to store more objects, you need to create a new, larger array and copy the previous array. The length of the array is stored in its member constant length.

There are two ways of creating an array. The first, and easiest way, is to simply define all the values it should contain. The second way is to use the new keyword and specify how large the array should be. Note that if you use latter method for non-built-in types, each position in the array is not initialized. In other words, every element is the special value null (corresponding to None in Python). For built-in types, each position is initialized to 0.

Let us take a look at a few examples of how to create an array:

Java

1:  // First method - specify each value
2:  int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
3:  
4:  // First method can also be used for non-built-in types:
5:  String[] heroes = { "Rincewind", "Vimes", "Zaphod", "Weatherwax", "Slartibartfast" };
6:  
7:  // Second method - use new
8:  int[] manyInts = new int[1024];
9:  
10: // Second method - each index now contains 'null'
11: String[] cities = new String[314];

As mentioned, an array has constant length. This means that primes can never hold more than ten elements, and each element has to be of type int. The same goes for the rest of the arrays, but note that even though heroes can never hold more than five Strings, the length of each String may vary.

If an array is of limited size, what do we do if we want to add more elements? What if another hero comes along? The answer is to create a new array, that can hold more elements, and then copy the elements of the smaller array into the larger one. Note that even though we can never put more elements into an array, we are not required to use every element -- null is a perfectly good value. This means that if it is likely that we will need more elements, we should create the array a little larger than we really need:

Java

String[] topTenHeroes = { "Rincewind", "Vimes", "Zaphod", "Weatherwax", "Slartibartfast", null, null, null, null, null };
topTenHeroes[5] = "Hrun the Barbarian";

[edit] Code analysis

On the first line, we create an array of Strings that can hold ten elements, but only uses the first five. On the second line, we use indexing to access the first unused element and set it to be a new hero.

We can do the same if we create the array using new. The following example illustrates:

Java

1: String[] heroes = new String[10];
2: int i = 0;
3: for (; i < 5; ++i) {
4:     heroes[i] = getNextHero();     // getNextHero() returns the next hero
5: }
6: heroes[i] = "Hrun the Barbarian";


[edit] Code analysis

On line 1, an empty array that can hold ten Strings is created. The array is initialised on lines 3-5, using the fictive function getNextHero(). Note that i is created outside the for loop, because we want to be able to access it after the loop is finished. If we had declared i inside the loop, it would have gone out of scope when execution breaks out of the loop. On line 6, we use i to access the first empty index and store a new hero in it.

After both examples the values in the array would be:

"Rincewind", "Vimes", "Zaphod", "Weatherwax", "Slartibartfast", "Hrun the Barbarian", null, null, null, null


Let us say ten heroes are not enough. Then we would have to create a new array, large enough to support more elements. The following code illustrates how to do this. The class is an implementation of a class for representing a guest list, and the problem encountered when we want to add more guests than we can have on the list.

Java

1:  class Guest {}
2:  
3:  class GuestList {
4:      private Guest[] repr;
5:      private int numElements;
6:  
7:      public GuestList() {
8:          repr = new Guest[50];
9:      }
10: 
11:     public GuestList(int numGuests) {
12:         repr = new Guest[numGuests];
13:     }
14: 
15:     public void add(Guest g) {
16:         // Are we full?
17:         if (numElements == repr.length) {
18:             // Increase length by 50 %
19:             Guest[] tmp = new Guest[repr.length + repr.length / 2];
20:             for (int i = 0; i < repr.length; ++i) {
21:                 tmp[i] = repr[i];
22:             }
23:             repr = tmp;
24:         }
25:         repr[numElements++] = g;
26:     }
27: 
28:     public void add(GuestList rhs) {
29:         if (rhs.numElements + numElements > repr.length) {
30:             // New length is the length of both lists plus 
31:             // half the length of the current list
32:             int newLength = rhs.repr.length + (3 * repr.length) / 2;
33:             Guest[] tmp = new Guest[newLength];
34: 
35:             // Copy our current representation
36:             for (int i = 0; i < numElements; ++i) {
37:                 tmp[i] = repr[i];
38:             }
39:             repr = tmp;
40:         }
41: 
42:         // Copy the right hand side representation
43:         for (int i = 0, j = numElements; i < rhs.numElements; ++i, ++j) {
44:             repr[j] = rhs.repr[i];
45:         }
46:         this.numElements += rhs.numElements;
47:     }
48: }

[edit] Code analysis

On line 1, we create an empty class Guest to represent a guest. The details of Guest are in any case irrelevant for GuestList. On lines 4 and 5, the two member variables of GuestList are declared: One array of Guests that holds the actual data, and an integer, numElements, for keeping track of how many Guests there are on the list. The reason for having a variable like numElements is that the size of the array does not tell us how much of that array we really use -- that is the task of numElements. This means that the number of free spaces in the array is repr.length - numElements.

We define two constructors for GuestList, one on lines 7-9 and one on lines 11-13. The former takes no argument, and initialises the array to hold 50 Guests. The latter takes one argument, the number of Guests, and allocates memory for repr to hold this many elements.

We have defined two functions for adding elements to our GuestList -- one function for adding a single Guest, defined on lines 15-26, and another for adding another GuestList, defined on lines 28-47. The first add()-function first tests if the array is full by comparing the number of elements to the maximum number of elements. If the array is full, it creates a new array of Guests, tmp, which is 50% larger than the previous array. It then copies each element from the previous array into the new one, before assigning the new array to be the array used as representation. On line 25, several things happen. We use indexing to access the first unused array element - because 0 is the first index, numElements is the last used element plus one. Because we use the post-increment operator, the value of numElements is first evaluated, and then incremented. The unused array element is then set to point to the Guest we are adding to the list. Note that because of the post-increment operator, line 25 could have been written as repr[numElements] = g; numElements += 1;

The second add() function does basically the same as the first, only that it operates on a range of new elements. First it tests if we can fit both the used elements in this.repr and the used elements in rhs.repr (rhs for right hand side) into repr. If not, the new array is created with capacity to hold both arrays, plus a small buffer, before the current representation is copied into this new array. Then this new array is set to be the representation used by the GuestList, before the elements of the other array are copied as well. Finally, numElements is updated properly with the number of added elements.

Note that in both functions, the size of the new array has been larger than it really had to. This is because creating a new array and copying elements is a relatively time-consuming task at runtime, and so we want to make sure that we only have to do it now and then.

[edit] Array Summary

  • Arrays are a less advanced kind of Python list.
  • Arrays are homogeneous, meaning they can only hold objects of one type.
  • Arrays have constant length. To fit more elements into an array, we have to create a new one.
  • Arrays are accessed through indexing. The first element is at index 0.
  • Arrays of non-built-in types created using new initially only contain null elements.

[edit] A New Kind of for Loop

In Java, there is a kind of for loop very similar to the for loop we know from Python. This loop is used to iterate over elements of a known type, for example an array. Let's go straight to the code:

Java

1: class NewFor {
2:     public static void main(String[] args) {
3:         String[] heroes = { "Rincewind", "Vimes", "Zaphod" };
4:         for (String h: heroes) {
5:             System.out.println(h);
6:         }
7:     }
8: }

Python

1: heroes = ["Rincewind", "Vimes", "Zaphod"]
2: for h in heroes:
3:     print h

[edit] Code analysis

On line 3, we create an array of Strings. On line 4, the for loop begins. h, of type String, is our counting variable, and for each iteration it is set to the next element in heroes. On line 5, h is printed. The result of the for loop is that each element in heroes is printed.


[edit] HashMaps

In Python, a dictionary, or dict, is a container type containing a set of unique keys, where each key has a value associated with it. In Java, such a data type is called a HashMap. Unlike the Python dict, the Java HashMap is not a built-in type, but is instead a part of the Java standard library. To access a HashMap, we need to import java.util.HashMap. A HashMap is created the usual way, by using new. There are a series of methods used to operate on a HashMap, as described in the following table:

Method signature Functionality
V put(K key, V value) Inserts a key/value-pair. If key already exists, the previous value is replaced by the new value, and the old value is returned.
V get(K key) Returns the value associated with key, or null if no such key/value exists.
V remove(K key) Returns the value associated with key, and removes the key/value pair from the HashMap.
boolean containsKey(K key) Returns true if the HashMap contains key, otherwise false.
boolean containsKey(V value) Returns true if the HashMap contains value, otherwise false.
int size() Returns the number of key/value pairs in the HashMap.
boolean isEmpty() Returns true if size() is 0, otherwise false.
void clear() Removes all key/value pairs from the HashMap.
Set keySet() Returns a Set of the keys in the HashMap. Note that changes to the Set affects the HashMap, and vice-versa.
Collection values() Returns a Collection of the values in the HashMap. Note that changes to the Collection affects the HashMap, and vice-versa.

You may wonder what a Set or a Collection is. Interested readers may read more about Sets and Collections at the official Java API specification. All we really need to know is that both Sets and Collections are iterable, and that we can use iterators over them.

[edit] Iterators

So what is an Iterator? An iterator is an auxiliary class used to iterate over a series of elements. An Iterator has two methods we use to achieve this: next(), which gives us access to the next element, and hasNext(), which returns true if there is one such element, otherwise it returns false. To get an Iterator from a Set or Collection, we use the iterator() method.


[edit] HashMap and types part one: Casting

A HashMap is inhomogeneous, which means that it may contain objects of different types. However, Java needs to know the type of the objects it operates on. This requires us to cast the return value of functions such as get(). This is achieved by prefixing an expression with a type name enclosed in parentheses. This is similar to changing a numerical type to float in Python in order to avoid integer division.

Let us have a look at an example illustrating the use of a HashMap, the use of iterators on the map, and casting.

Java

1:  import java.util.*;
2:   
3:  class HashMapDemo {
4:      public static void main(String[] args) {
5:   
6:          HashMap books = new HashMap();
7:          books.put("The Light Fantastic",
                "Terry Pratchett");
8:          books.put("His Dark Materials", 
                "Philip Pullman");
9:          books.put("The Design and Evolution of C++", 
                "Bjarne Stroustrup");
10:         books.put("101 Uses for a Dead Cat", 
                "James Bond");
11:         books.put("So Long, and Thanks for All the Fish", 
                "Douglas Adams");
12:         books.put("The Sisterhood of the Traveling Pants",
                "Ann Brashares");
13:  
14:         // Look up the author of "Kalkulus"
15:         String someBook = "Kalkulus";
16:         String someAuthor = (String)books.get(someBook);
17:         if (someAuthor == null) {
18:             System.out.println("Sorry, I have not heard about that book");
19:         } else {
20:             System.out.print(someBook + " is written by ");
21:             System.out.println((String)books.get(someBook));
22:         }
23: 
24:         // Alternative way of writing the above
25:         if (books.containsKey(someBook)) {
26:             System.out.print(someBook + " is written by ");
27:             System.out.println((String)books.get(someBook));
28:         }
29:  
30:         // Print the HashMap
31:         Iterator i = books.keySet().iterator();
32:         while (i.hasNext()) {
33:             String book = (String)i.next();
34:             System.out.print("\"" + book + "\" is written by ");
35:             System.out.println((String)books.get(book));
36:         }
37:  
38:         // "101 Uses for a Dead Cat" isn't written by James Bond
39:         // It is written by Simon Bond
40:         books.put("101 Uses for a Dead Cat", "Simon Bond");
41:  
42:         // C++ isn't really all that great. Let's remove the book
43:         books.remove("The Design and Evolution of C++");
44:  
45:         System.out.println("There are " + books.size() + " books.");
46: 
47:         // Remove everything
48:         books.clear();
49:         System.out.println("Is the HashMap empty? " + books.isEmpty());
50: 
51:     }
52: }

Python

1:  books = { \
2: "The Light Fantastic" : "Terry Pratchett",\
3: "His Dark Materials" : "Philip Pullman",\
4: "The Design and Evolution of C++" :\
       "Bjarne Stroustrup",\
5: "101 Uses for a Dead Cat" : "James Bond",\
6: "So Long, and Thanks for All the Fish" :\
       "Douglas Adams",\
7: "The Sisterhood of the Traveling Pants" :\
       "Ann Brashares" }
8:  
9   # Look up the author of "Kalkulus"
10: someBook = "Kalkulus"
11:  someAuthor = books.get(someBook)
12: 
13: if someAuthor == None:
14:     print "Sorry, I have not heard about that book"
15: else:
16:     print someBook, "is written by", books[someBook]
17: 
18  # Alternative way of writing the above
19: if someBook in books:
20:     print someBook, "is written by", books[someBook]
21: 
22  # Print the dict
23: for i in books:
24:     print i, "is written by", books[i]
25: 
26: # "101 Uses for a Dead Cat" isn't written by James Bond
27: # It is written by Simon Bond
28: books["101 Uses for a Dead Cat"] = "Simon Bond"
29:
30: # C++ isn't really all that great. Let's remove the book
31: del books["The Design and Evolution of C++"]
32: 
33: print "There are", len(books), "books."
34: 
35: # Remove everything
36: books.clear()
37: print "Is the dictionary empty?", (len(books) == 0)

[edit] Code analysis

On lines 6-12 the HashMap is created and filled with six book/author pairs. On line 16, the author of someBook is read from the map. Note that we have to cast the return type of get() before assigning it to someAuthor. On line 17, someAuthor is tested: If it is null, we have not stored the author of someBook. If it isn't null, the author is printed on lines 20 and 21.

Lines 24-28 achieves the same as the previous block, but in less code. On line 25 we check if the map contains the key described by the variable someBook. If it does, we print the author, using the key. The entire HashMap is printed on lines 30-36. To print each key/value pair, we first get an Iterator to iterate over the keys. We then use those keys to access the value associated with each key. On line 40, the value associated with "101 Uses for a Dead Cat" is changed, and on line 43 the key/value pair whose key is "The Design and Evolution of C++" is removed. Finally, we print the size of the HashMap, and then empty it.


[edit] HashMap and types part two: Locking the HashMap

If we want our HashMap to be homogeneous, i.e. to make sure that all keys and all values have the same type, we can lock it. This is achieved by appending the desired types enclosed in brackets to HashMap when we create it. If a locked HashMap is fed a key/value pair of the wrong type, a compiler error is generated. By locking a HashMap we also allow users of the map to use the Python-like for loop on the values or keys of the map. Let us look at an example of a locked HashMap, and a for loop iteration over it:

Java

1:  import java.util.*;
2:  
3:  class LockedHash {
4:      public static void main(String[] args) {
5:          // Create and fill the HashMap
6:          HashMap<String, String> airplanes = new HashMap<String, String>();
7:          airplanes.put("LN-DAY", "Cessna 172 Skyhawk");
8:          airplanes.put("LN-NRK", "Piper PA38-112 Tomahawk");
9:          airplanes.put("LN-KAP", "Mudry CAP 10B");
10:         airplanes.put("LN-ACL", "PA19 Piper Cub");
11:         // airplanes.put(280688, "Batmobile"); -- ERROR! 280688 is not a String
12: 
13:         // Print all the values
14:         for (String s: airplanes.values()) {
15:             System.out.println(s);
16:         }
17:     }
18: }

Python

1:  # Create and fill a dict
2:  airplanes = { \
3:      "LN-DAY" : "Cessna 172 Skyhawk",\
4:      "LN-NRK" : "Piper PA38-112 Tomahawk",\
5:      "LN-KAP" : "Mudry CAP 10B",\
6:      "LN-ACL" : "PA19 Piper Cub" }
7:  
8:  # Print the values
9:  for s in airplanes:
10:     print airplanes[s]

[edit] Code analysis

The code should be fairly straightforward. On lines 6-10 a HashMap of String/String pairs is created and filled. Line 11 illustrates illegal code: We cannot add an int/String pair to the locked map. On lines 13-16 we iterate over the values of the map using the special for loop, and print each one of them.

[edit] HashMap Summary

  • A HashMap is the Java equivalent of a Python dict, containing key/value pairs.
  • We need to import java.util.HashMap; to work on a HashMap, and generally java.util.*; if we want access to Iterator etc.
  • A HashMap is not homogeneous, unless we lock it to certain types.
  • If it is not locked, we need to cast elements returned from the map before we use them.
  • There are no guarantees regarding the order of elements in a HashMap. In particular, there are no guarantees that the order remains constant.
  • The keys and values can be accessed using the keySet() and values() methods, respectively. The return values of these methods can be iterated over using an Iterator.
  • A HashMap is dynamically sized, and allocates more memory by itself as it grows. The size is accessed through the size() method.
  • A HashMap may not contain elements of built-in types. We can not have a HashMap of ints or chars.


[edit] String theory

Working with text is a common task in most computer programs. We usually need to output text, read text, manipulate the text, format the text, etc. To simplify these operations, we have the String class which represents a string of text. As in Python, a String is just a series of characters, with methods to operate on these characters. Note that Strings are immutable: They cannot be changed. Changes to a String are returned as a new String object.

Method signature Functionality
char charAt(int index) Returns the character at index.
String substring(int from, int to) Returns a new String of the characters from index from up to, but not including, to.
String substring(int from) Returns a new String of the characters from index from to the end of the String.
int compareTo(String str) Makes a lexicographical comparison of two Strings.
String toUpperCase() Returns this String with all letters transformed to uppercase.
String toLowerCase() Returns this String with all letters transformed to lowercase.
boolean equals(Object other) Returns true if this String is equal to other, otherwise false.
boolean equalsIgnoreCase(Object other) Returns true if this String is equal to other, ignoring case, otherwise false.
int indexOf(String str) If other is a substring of this String the index of the first occurrence is returned. Otherwise -1 is returned.
boolean startsWith(String prefix) Returns true if this String begins with prefix, otherwise false.
boolean endsWith(String suffix) Returns true if this String ends with suffix, otherwise false.
String valueOf(type object) Returns a String representation of object.
String[] split(String regex) Returns an array of Strings containing this String split around occurrences of regex.

(For a complete description, look at the Java API specification for String.) We will take a closer look at these methods through a series of examples.


Java

1: String text = "When Chuck Norris hits water, he doesn't get wet. Water gets Chuck Norris.";
2: String chuck = text.substring(5, 17);
3: String water = text.substring(50);

[edit] Code analysis

We define three Strings. On the first line, we create a String to hold the text we work on. On the second line, we create a new String from the substring from index 5 to index 17. On the third line, we create a new String from the substring from index 50 and to the end of text. The value of chuck will be "Chuck Norris", and the value of water will be "Water gets Chuck Norris.".


Java

1: String hero = "Jason";
2: System.out.println(hero.equals("Jason"));
3: 
4: String text = "Quidquid latine dictum sit, altum viditur.";
5: System.out.println(text.startsWith("quidquid"));
6: System.out.println(text.endsWith("viditur."));

[edit] Code analysis

On line 1, a String with value "Jason" is created. This is tested against "Jason" on line 2. On line 4, a String holding a text is created. Line 5 prints whether or not it starts with "quidquid", which is false, because of the lower-case q in the parameter. Line 6 prints whether or not text ends with "viditur.", which is true.


[edit] Exercises

(The numbers in parentheses are the exercise numbers from Langtangen's Elements of Computer Programming.)

1 (1.2):

Write a Java program that prints Hello World to the screen.

2:

Write a program that asks the user for his name, and then prints that name to the screen.

3 (2.1):

Write a program that generates all odd numbers from 1 to n. Set n in the beginning of the program and use a while loop to compute the numbers. (Make sure that if n is an even number, the largest generated odd number is n-1.)

4 (2.2):

Modify the program from exercise 3 such that you first generate odd numbers and store them in an array, and then print the array to the screen. Note: In order to print an array, you need to iterate through the elements and print one at a time.

5 (2.10):

The formula for converting fahrenheit degrees to celsius reads C = 5/9 * (F-32). Write a function C(F) that implements this formula. Demonstrate that C(F(c)) for a few arbitrarily chosen real numbers equal c.

6 (2.15):

Write a function sum that iterates through an array of doubles using a for loop and computes the sum of each element.

7 (2.16):

Rewrite exercise 6 using a while loop.

8 (8.4):

Consider a quadratic function f(x; a, b, c) = ax^2 + bx + c. Make a class Quadratic for f where a, b, and c are attributes, and the methods are:
  1. value for computing a value of f at a point x,
  2. table for writing out a table of x and f(x) for n x values in [L,R],
  3. roots for computing the two roots.

9 (9.31):

Make a class for drawing coloured balls out of a hat, that could be used in the following way:
// Make a hat with balls of 3 colours, each colour appearing on 4 balls:
String[] colours = { "red", "white", "blue" };
Hat h = new Hat(colours, 4);
// Draw a random ball
h.draw();

Exercises from Rett på Java:

  • 2.3
  • 2.5
  • 2.6
  • 3.7
  • 4.2
  • 4.3
  • 4.8
  • 5.1
  • 5.2
  • 5.3
  • 5.7
  • 7.4
  • 7.8
  • 8.1
  • 8.2
  • 8.3
  • 8.7 b, c
  • Rewrite exercise 4.3 using HashMap
  • 9.3

[edit] Further reading


Java

(template)

Python

(template)