Advanced Java lectures

Advanced Java exercises

Enumerations

Create a class with a main method. For all of the exercises below, put code in main that tests the new content.

  1. Create a MyDate class
    1. In it, create enums Month and Weekday
  2. Create methods
    1. Names
      1. Month::getEnglishName
        1. Month::getHunName,
        2. Month::getXName (where X is your favourite language),
        3. replace the above methods using an enum Language
        4. make similar methods for Weekdays
    2. MyDate::getWeekDay
    3. Weekday::isThisDay(Month month, int day)
  3. Write Javadoc documentation for the methods

Serialization

Make a serializable class Book that holds the following information:

The class BookStore stores books in a hash table, identifying them by their names. The class should implement the following methods:

// Puts some predefined books into the hash table, to serve as initial data.
void addSomeBooks();

// Loads the books from the given file.
// Returns whether the load operation was successful.
boolean loadBooks(String fileName);

// Writes the books into the given file.
// Returns whether the save operation was successful.
boolean saveBooks(String fileName);

Make a command line application that tests the above operations.

TCP/IP

Make a simple client-server application whose parts communicate using TCP/IP. Use the classes Book and BookStore from the previous exercise.

The server accepts clients one after the other. When a client connects, the server does the following.

  1. Takes the name of a book from the client.
  2. Creates a BookStore.
  3. Tries to load the books from a file; if unsuccessful, the store is initialised using addSomeBooks.
  4. Looks up the requested book by name in the store.
    1. If found, sends true to the client, then the book itself.
    2. Otherwise, sends false to the client.
  5. Saves the books to the file.

The client connects to the server, and takes the appropriate steps, mirroring the server.

Threading

  1. Make two threads that each use System.out.println to print a given text 10000 times. (Give different texts to the threads.)
  2. Now, instead of println, print each text character by character (and an ending newline).
    1. Use synchronization to fix the issue.
  3. Two goats are trying to cross a narrow bridge. They meet in the middle, and they start pushing each other. Independently of each other, the goats take some time, and then push the other goat one step. Whenever one of the goats is pushed off of the bridge, the game ends. The speed of the goats (how much they wait between pushes) and the length of the bridge are command line parameters; make the program log important actions.
  4. Compute the nth Fibonacci number so that the non-basic cases compute the two halves of the addition on two threads.
  5. Make a TCP/IP server that can handle several clients: after accepting them, each connection is handled by a separate thread. The client sends a number, and the server replies with the corresponding Fibonacci number.

Producer-consumer

Use wait and notifyAll in the following exercises.

  1. Make two producer threads that each read words from a file. The producer threads produce a word in random intervals, waiting 300-2500 msec between each. Make a consumer thread that takes two words (one from each producer), and writes them into a file.
    1. Extend the previous exercise so that there are n producers and n files.
    2. Extend the previous exercise so that there are m consumers.
      1. The consumers are racing each other: each n-tuple of produced words will be taken by only one consumer.
      2. The consumers are not racing each other: each n-tuple of produced words will be taken by all consumers.
  2. Make a program for the dining philosophers. Try to work out a solution before reading up on possible solutions.

Anonymous functions (Lambda functions)

  1. Create the method compose that takes two functions and returns their composition, which is another function.
  2. Create the method iterate that takes a function f and a (nonnegative) integer n and returns a function that iterates f n times.
  3. Sort the command line arguments based on the number of 'a' characters in them.
  4. Create the method createArray that fills in an array. It takes the length of the array as an argument, and an int->int function f. Use the f function on each index of the array, and it will produce the value for the index.
    1. Make an array of arrays in a similar way. Here, the function has to take two arguments for the two indices.
    2. Can you make createArray generate an array of arbitrary depth?
  5. Create the method geneticAlgorithm that runs a genetic algorithm. The algorithm takes a number of arguments.
    1. First, populationCount entities are created using createRandomEntity().
    2. Then, doCrossover(Entity e1, Entity e2) is invoked on two random entities. This is repeated crossoverCount times.
    3. Then, mutateEntity(Entity e) is invoked on a randomly selected entity. The method, with mutationProbability probability will change the entity in some way. (Usually, mutationProbability is quite low, perhaps 0.1%.)
    4. Then, calculateFitness(Entity e) calculates the fitness of each entity. We only keep the best pruneCount entities; we fill up the remaining places with createRandomEntity().
    5. So far, we have generated a new generation out of the previous one. We repeat these steps generationCount times, then return with the entity with the highest finess value of the last generation.
  6. As a concrete Entity type, use Strings. Your goal is to find a given String goal; the fitness values show how far our String is from the goal: let it be the difference of the lengths of the strings, plus the number of differing character positions. (Therefore, the best fitness value is 0, which is reached when we have reached the goal.) A crossover splits both strings at random positions, and joins their first and second parts. A mutation changes a random character in the string.

Streams

  1. Print the lengths of the command line arguments in reverse order.
  2. Print the sum of even numbers that are greater than 8 from the command line arguments.
  3. Make a method that generates a stream of the first n prime numbers.
  4. Zip two streams together: make a stream of pairs that contains the first two elements as a pair, the second two etc. If one of the streams end, the zipped stream should end, too.
  5. Given some filenames in the command line arguments, print the names on one line, separated with commas, in descending order in the number of lines in the file. If two files have the same number of lines, then the one with more words on its first line should come first.

Aspects

Install aspects into Eclipse by opening Help>Install New Software, pasting http://download.eclipse.org/tools/ajdt/44/dev/update in the box, and selecting the required packages.

  1. Compute the 5th Fibonacci number the recursive way. For each recursive call, print the return value.
  2. The Airplane class contains a fuel field, and its getMaxFlightDistance method returns how many kilometres it can fly. We have an instance gimliGlider, whose fuel field was unfortunately set in pounds, but because of the change in units, the return value of the getMaxFlightDistance, which uses the fuel field, expects its value in kilograms. Can you help the plane land in safety?
  3. Make an aspect PrintFirstArgs that is activated when a method in class Test is called that has at least one parameter. The aspect should print the first parameter’s value on the command line.
  4. Make the CallerCallee aspect, which counts how many method calls in the code are such that the caller object and the callee object are the same. For each such call, print the counter’s current value to the standard output.
  5. Make an aspect ExceptionLog that logs into a file each exception that is thrown by the methods of the program.

Templates

  1. Make the genetic algorithm more abstract: make the Entity class a template parameter.
  2. A Map<T,T> and a Function<T,Optional<T>> are very similar to each other. Given a value of type T (the key of the Map; the parameter of the Function), both can say that this value is not present (the Map doesn’t have the key; the Function returns an empty Optional), or they will associate it with another value of type T. Make a class EmulateMapWithFunction whose constructor takes such a Map, and its method checkAttempt will take a Function and try to find a difference between the two.
    1. The constructor also takes a Supplier<T> parameter. As a first step, checkAttempt uses this Supplier to generate a value currentValue.
    2. Then, it tries to “take a step” with both the Map and the Function from currentValue.
      • If the Map can take a step and the Function can’t, or the Map can’t and the Function can, then we have found a difference, and checkAttempt returns the currentValue, where the difference was found.
      • If both the Map and the Function can’t take a step, we couldn’t find a difference, and checkAttempt returns an empty Optional.
      • If we could take a step, but the Map and the Function take us to different values, then we have found a difference, and checkAttempt returns the currentValue, where the difference was found.
      • Otherwise, we didn’t find a difference at this point: the Map and the Function take us to the same element. Take it as a new currentValue, and continue taking steps.
        • If we get to a currentValue that we have already seen, checkAttempt should return an empty Optional, otherwise it would loop infinitely.
  3. Implement an AVL tree data structure, or, if you are feeling adventurous, a red-black tree (another description is here, but don’t look at the example codes) data structure.

Logging

  1. Change checkAttempt (in EmulateMapWithFunction) to log all of the values that it progresses through.
    1. Go back to the original checkAttempt, and make an aspect that will take care of the logging. Use a data field in EmulateMapWithFunction to store the most recently checked value.
    2. Make the code so that the value progression of each run of the checkAttempt are logged separately. Produce a file that contains the progressions with the accepted runs first, and then the rejected runs afterwards; in both parts, the progressions should be ordered by the number of progression steps.

Testing

  1. Change geneticAlgorithm so that it also takes an entity as a parameter; this entity should always be a member of the population (that is, it should not be thrown out even if its fitness is too low).
    1. Check with JUnit that the fitness of the return value of geneticAlgorithm is not less than that of this entity.
    2. Extract the body of the loop in geneticAlgorithm into a separate function. Make a JUnit test case that checks that the size of the population at the beginning of every loop iteration equals the population count (which, for the sake of simplicity, should be the first parameter of geneticAlgorithm).