Skip to content

Instantly share code, notes, and snippets.

@arthurgousset
Last active April 26, 2023 09:17
Show Gist options
  • Save arthurgousset/5360808b53413105bffafe7a5df6efa4 to your computer and use it in GitHub Desktop.
Save arthurgousset/5360808b53413105bffafe7a5df6efa4 to your computer and use it in GitHub Desktop.
Java Noob Notes #noob

🤷‍♂️ Java (noob notes)

Quick link: Oracle Java 19 Docs

Formatting and style

Indentation

Line wrapping for if statements should generally use the 8-space rule, since conventional (4 space) indentation makes seeing the body difficult. For example:

//USE THIS INDENTATION INSTEAD
if ((condition1 && condition2)
        || (condition3 && condition4)
        ||!(condition5 && condition6)) {
    doSomethingAboutIt();
} 

Source: Oracle Code Conventions

Naming

  • File: <Class>.java
  • Class: capitalised
  • Method: lowercase camelcase

Ternary operator

Example:

int i = 10;
return i > 0 ? true : false; // positive and negative evaluation
int val1 = 10;
int val2 = 20;

int max = val1 >= val2 ? val1 : val2; // max calculation

Source: Jenkov.com

Command line arguments String[] args (for me)

espresso@FVFG31YLQ05P POP2_Programming % cd probs02-dist                         
espresso@FVFG31YLQ05P probs02-dist % java CountWords.java test.txt output.txt
test.txt
output.txt

Source: Stack Exchange

VS Code Launch.json doesn't work (for me)

Instructions like the below are easier on IntelliJ and require a couple manual settings on VS Code.

Write a program that reads all words in a file (specified as the first argument) and prints out, for each word, the line numbers on which the word occurred. This output should also be written to a second file (specified as the second argument). By the arguments, we refer to the strings in the array String[] args that is passed to the main method.

You can set args in a launch.json file. To create a file, you can follow simple instruction in VS Code debugging.

The setting is:

args - The command-line arguments passed to the program. Use "${command:SpecifyProgramArgs}" to prompt for program arguments. It accepts a string or an array of string.

Source: Launch

Example:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        
        {
            "type": "java",
            "name": "Launch Current File",
            "request": "launch",
            "mainClass": "${file}",
            "args": ["test.text", "output.txt"]
            // "args": "${command:SpecifyProgramArgs}"
            /* 
            To prompt for program arguments. It accepts a string or an array of string.
            Source: https://code.visualstudio.com/docs/java/java-debugging#_configuration-options
             */
        },
        {
            "type": "java",
            "name": "Launch CountWords",
            "request": "launch",
            "mainClass": "CountWords",
            "projectName": "POP2_Programming_b4a0f6be"
        }
    ]
}

Loops

for

for (1. initialise; 2. if condition; 4. then iterate) {
    // 3. do statement
}
  1. initialises variable (can be initialised elsewhere)
  2. checks condition
  3. executes statements
  4. iteration

Source: POP2 Session 1

break statement

The continue statement breaks one iteration (in the loop), if a specified condition occurs, and continues with the next iteration in the loop.

public class Main {
  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      if (i == 4) {
        continue;
      }
      System.out.println(i);
    }  
  }
}
// Output
0
1
2
3
5
6
7
8
9

Source: W3School

break

The break statement completely jumps out of a loop.

public class Main {
  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      if (i == 4) {
        break;
      }
      System.out.println(i);
    }  
  }
}
// Output
0
1
2
3

Source: W3School

for each

// for each
for (<type> <name> : arr) { 
  // make use of <name> variable;
}

// equivalent to normal for loop
for (int i = 0; i < arr.length; i++) { 
  <type> <name> = arr[i];
  // make use of <name> variable;;
}

// Example 
public static int maximum(int[] numbers){
  
  // defines variable
  int max = numbers[0];
  
  // for each iteration
  for (int num : numbers){
    if (num > max) {
      max = num;
    }
  }
  
  return max;

Limitation:

  • Not appropriate when you want to modify the array
for (int num : marks) {
  // only changes num, not the array element
  num = num*2; 
}
  • Only iterates forward over the array in single steps

Source: Geekforgeeks

switch

  • The default keyword specifies some code to run if there is no case match
  • When Java reaches a break keyword, it breaks out of the switch block. This will stop the execution of more code and case testing inside the block.
int day = 4;

switch (day) {
  case 1:
    System.out.println("Monday");
    break;
  case 2:
    System.out.println("Tuesday");
    break;
  case 3:
    System.out.println("Wednesday");
    break;
  case 4:
    System.out.println("Thursday");
    break;
  case 5:
    System.out.println("Friday");
    break;
  case 6:
    System.out.println("Saturday");
    break;
  case 7:
    System.out.println("Sunday");
    break;
}
// Outputs "Thursday" (day 4)

Source: W3School

Generics

Generic classes

From POP2:

Generic code is code that is parameterized by a type parameter

For example, an ArrayList is a generic class which takes a type argument To declare an ArrayList, we write e.g. ArrayList<String> list; or ArrayList<Integer> list; An ArrayList supports various operations (add, remove, size, ...) 👉 Behavior is independent of the types of the objects held in the list

Example:

// Generic class: "GenericOne"
public class GenericOne<T> {
    // attribute
    T obj;
    
    // constructor
    GenericOne( T o ) {
        obj = o;
    }

    // method
    void printType() {
        System.out.println( "obj has type: " +
            obj.getClass().getName());
    }
    
    // other methods
    // ...
}

public static void main(String[] args) {
    // instances of generic class
    GenericOne<String> a = new GenericOne<String>("Joe");
    GenericOne<Integer> b = new GenericOne<Integer>(89);

    // method calls
    a.printType()
    b.printType()
}
// Output
obj has type: java.lang.String
obj has type: java.lang.Integer

Source: POP2, credit Hubie Chen

Bounded types (e.g. <T extends Number>)

Motivation: We can't call the .convertToInt() method (below) on arbitrary instances of type T because the operation is only supported on numeric classes in Java (i.e. within superclass Number). We get a compile error:

|  Error:
|  cannot find symbol
|    symbol:   method intValue()
|          return num.intValue();

So we restrict T to only support numeric classes, i.e. subclasses of Number.

public class SomeGenericClass<T extends Numeric>

From POP2:

This mandates that T can only be instantiated by subclasses of Number

  • extends allows us to specify an upper bound
  • super allows us to specify a lower bound

From Stack Overflow:

I have an X in my hand. If I want to write my X into a List, that List needs to be either a List of X or a List of things that my X can be upcast to as I write them in i.e. any superclass of X...

List<? super   X>

If I get a List and I want to read an X out of that List, that better be a List of X or a List of things that can be upcast to X as I read them out, i.e. anything that extends X

List<? extends X>
// public class Multiplier<T> { // <-- Doesn't work
public class Multiplier<T extends Number> { 
    // attribute
    T num;

    // constructor
    Multiplier( T num ) {   
        this.num = num;   
    }

    // getter method
    T getNum() {
        return num;
    }
    
    // method
    int convertToInt() {
        return num.intValue();
    }
    
    // other methods
    // ...
}

Generics classes

General form of method declaration:

// Form: <type parameters> return_type name(parameters)

// Example 1
<T> void doSomething(T obj) {
  //  ... 
}

// Example 2
<T, V extends T> boolean compareArrays(T[] a, V[] b) {
  // ...
}

Wildcards

Here the type U is not used explicitly in the method body, so we can simply replace it with ?.

// Redundant use of `U`
<U extends Number> boolean absEqual(Reciprocate<U> o) {
    return( Math.abs( num.doubleValue() ) 
        == Math.abs( o.getNum().doubleValue() ) );
}

// Replace with wildcard `?` instead
boolean absEqual(Reciprocate<? extends Number> o) {
    return( Math.abs( num.doubleValue() ) 
        == Math.abs( o.getNum().doubleValue() ) );
}

Generic interfaces

From POP2 (briefly):

// interface definition
interface Containment<T>  {
    boolean contains( T obj );
}
// Implementation 1
class AClass<T> implements Containment<T> { // OK
  // ...
} 

// Implementation 2
class AClass implements Containment<T> { // not OK class 
  // ...
}

// Implementation 3
AClass implements Containment<Integer> { // OK
  // ...
}

// Implementation 4
class AClass<T extends Number> implements Containment<T> { // OK
  // ...
}
    

// Implementation 4
class AClass<T extends Number> implements Containment<T extends Number> { // not OK
  // ...
}

Lambda expression

From W3School:

A lambda expression is a short block of code which takes in parameters and returns a value.

Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.

Source: W3School

The simplest lambda expression contains a single parameter and an expression:

parameter -> expression

Expressions are limited. They have to immediately return a value, and they cannot contain variables, assignments or statements such as if or for.

To use more than one parameter, wrap them in parentheses:

(parameter1, parameter2) -> expression

To do more complex operations, a code block can be used with curly braces. If the lambda expression needs to return a value, then the code block should have a return statement.

(parameter1, parameter2) -> { code block }

Example:

// Some ArrayList instance: 
// e.g. ArrayList<String> lst = ArrayList<String>();
lst.removeIf( s -> lst.indexOf(s) % 2 == 0);

Lambda expressions and functional interfaces

From POP2:

A lambda expression:

  • is an anonymous, unnamed method in Java
  • often used to implement a method defined by a functional interface

A functional interface:

  • is an interface that contains exactly one abstract method
  • E.g. the interface Runnable is a functional interface; it defines just one method, run(), which is abstract
  • A functional interface can be used to define a target type for a lambda expression
interface MyValue
{
    double getValue();
}

interface IntTest
{
    boolean test( int a, int b );
}

public class LambdaDemo {

    public static void main(String[] args) {
        MyValue myVal;
        myVal = () -> 98.6;
        System.out.println( "A constant value: " + myVal.getValue());

        IntTest isFactor;
        isFactor = (d, n) -> (n % d) == 0;
        if( isFactor.test( 2, 10 ))
        {
            System.out.println( "2 is a factor of 10." );
        }

        IntTest lessThan = (n, m) -> (n < m);
        if( lessThan.test( 2, 10 ))
        {
            System.out.println( "2 is less than 10." );
        }

        // example of block lambda
        // lambda returns true if d is the smallest factor of n
        //   which is strictly greater than 1
        IntTest isSmallestFactor = (d, n) -> {
            if( d <= 1 ) return false;
            if( !isFactor.test( d, n )) return false;
            for( int i = 2; i < d; i++ )
            {
                if( isFactor.test( i, n )) return false;
            }
            return true;
        };

        for( int i = 0; i <= 9; i++ )
        {
            System.out.println( "isSmallestFactor.test on " + i + ",9: " +
                    isSmallestFactor.test( i, 9 ));
        }
    }
}

Source: POP2, credit Hubie Chen

Predefined functional interfaces

  • java.util.function has several predefined functional interfaces
    • UnaryOperator<T> - defines method apply which applies a unary operation to an object of type T and returns the result (of type T)
    • BinaryOperator<T> - similar to previous interface, but for applying an operation to two objects
    • Consumer<T> - defines method accept(); applies an operation to an object of type T
    • Predicate<T> - defines method test which applies an operation to an object of type T and returns a boolean value

From java.util.function:

There are several basic function shapes, including:

  • Function (unary function from T to R),
  • Consumer (unary function from T to void),
  • Predicate (unary function from T to boolean), and
  • Supplier (nullary function to R).

Function shapes have a natural arity based on how they are most commonly used. The basic shapes can be modified by an arity prefix to indicate a different arity, such as:

  • BiFunction (binary function from T and U to R).

There are additional derived function shapes which extend the basic function shapes, including:

  • UnaryOperator (extends Function), and
  • BinaryOperator (extends BiFunction).

See more at section java.util.function (down).

import java.util.function.*;

public class PredefinedDemo {

    public static void main(String[] args) {

        BinaryOperator<Integer> mult = (a, b) -> a * b;

        Consumer<Integer> printer = (a) -> System.out.println( a );

        for( int i = 0; i <= 5; i++ ) {
            printer.accept( mult.apply( i, 3 ));
        }

        Predicate<Integer> isEven = (n) -> (n % 2) == 0;
        
        for( int i = 0; i <= 5; i++ ) {
            System.out.println( isEven.test( mult.apply( i, 3 )));
        }
    }
}

Source: POP2, credit Hubie Chen

Variable capture

Variables defined in the scope enclosing a lambda expression can be used inside the lambda expression

Only local variables that are “effectively final” can be so used.

interface IntFunc {
    int func( int n );
}

public class VariableCapture {

    public static void main(String[] args) {

        int num = 10;
        // can't modify num here
        IntFunc lambda = (n) -> {
            // can't modify num here
            int v = num + n;
            return v;
        };
        System.out.println( lambda.func( 8 ));

        lambda = getAdder( 100 );
        System.out.println( lambda.func( 8 ));
    }

    public static IntFunc getAdder( int a ) {
        IntFunc lambda = (n) -> { return n + a; };
        return lambda;
    }
}

Source: POP2, credit Hubie Chen

Exceptions and lambda expressions

From POP2:

A lambda expression can throw an exception.

If it throws a checked exception, the functional interface must declare that the abstract method throws it

import java.io.*;

interface IOAction
{
    boolean ioAction( Reader rdr ) throws IOException;
}
// recall that Reader is an abstract class for dealing with character streams

public class LambdaException {
    public static void main(String[] args) {
        IOAction action = (r) -> {
            int ch = r.read();
            return true;
        };
    }
}

Source: POP2, credit Hubie Chen

Method references (e.g. ClassName::methodName)

A method reference provides a way to refer to a method without executing it. A method reference (when evaluated) provides an instance of a functional interface

Kind Syntax Examples
Reference to a static method ContainingClass::staticMethodName Person::compareByAge MethodReferencesExamples::appendStrings
Reference to an instance method of a particular object containingObject::instanceMethodName myComparisonProvider::compareByName myApp::appendStrings2
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName String::compareToIgnoreCase String::concat
Reference to a constructor ClassName::new HashSet::new

Syntax:

  • Reference to a static method: ClassName::methodName
  • Reference to an instance method of an object: objectReference::methodName
  • Reference to a constructor: Classname::new

If we have:

interface MyFunc {
    MyClass func( String s );
}

and we have that class MyClass has a corresponding constructor:

class MyClass {
    MyClass( String s ) { 
        // some code here 
        // ...
}

Then, we could write:

MyFunc mf = MyClass::new;

Bigger example:

interface IntPredicate
{
    boolean test( int n );
}

class ValueStore
{
    private int value;

    ValueStore( int x )
    {
        value = x;
    }

    void updateValue( int x )
    {
        value = x;
    }

    boolean isValue( int a )
    {
        return a == value;
    }

    static boolean isPositive( int n )
    {
        return n > 0;
    }

}

public class ReferenceDemo {
    static boolean evaluate( IntPredicate pred, int num )
    {
        return pred.test( num );
    }

    public static void main(String[] args) {
        IntPredicate p = ValueStore::isPositive;
        System.out.println( p.test( 3 ));
        System.out.println( p.test( -3 ));
        System.out.println( evaluate( p, -3 )); // behaves like previous line

        ValueStore store = new ValueStore( 7 );
        p = store::isValue;
        System.out.println( p.test( 7 ));
        store.updateValue( 10 );
        System.out.println( p.test( 7 ));

    }
}

Source: POP2, Hubie Chen

Exceptions

From GeekforGeeks:

An exception is an unwanted or unexpected event, which occurs during the execution of a program i.e at run time, that disrupts the normal flow of the program’s instructions. In Java, there are two types of exceptions:

  • Checked exceptions
  • Unchecked exceptions

Inheritance structure:

  • Object
    • Throwable
      • Exception
        • Checked exception (e.g. IO or Compile time exception)
        • Unchecked exception (e.g. NullPointerException or Runtime exception)
      • Error
        • Virtual Machine error
        • Assertion error

Checked Exceptions

These are the exceptions that are checked at compile time. If some code within a method throws a checked exception, then the method must either handle the exception or it must specify the exception using the throws keyword.

public static void main(String[] args)
        throws IOException
    {
  
        // Creating a file and reading from local repository
        FileReader file = new FileReader("C:\\test\\a.txt");
  
        // Reading content inside a file
        BufferedReader fileInput = new BufferedReader(file);
  
        // Printing first 3 lines of file "C:\test\a.txt"
        for (int counter = 0; counter < 3; counter++)
            System.out.println(fileInput.readLine());
  
        // Closing all file connections
        // using close() method
        // Good practice to avoid any memory leakage
        fileInput.close();
    }

Source: GeekforGeeks

Unchecked Exceptions

These are the exceptions that are not checked at compile time. Consider the following Java program. It compiles fine, but it throws ArithmeticException when run. The compiler allows it to compile because ArithmeticException is an unchecked exception.

In Java exceptions under Error and RuntimeException classes are unchecked exceptions, everything else under Throwable is checked.

public static void main(String args[])
    {
        // Here we are dividing by 0
        // which will not be caught at compile time
        // as there is no mistake but caught at runtime
        // because it is mathematically incorrect
        int x = 0;
        int y = 10;
        int z = y / x;
    }
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.main(Main.java:5)
Java Result: 1

Source: GeekforGeeks

Inheritance

Subclass Constructors

Invocation of a superclass constructor must be the first line in the subclass constructor.

The syntax for calling a superclass constructor is:

  • super(); the superclass no-argument constructor is called, or
  • super(parameter list);: the superclass constructor with a matching parameter list is called.

Source: Oracles Java Tutorials

// < Child >.java
public class < Parent > {
    
    // attributes
    int param1;
    int param2;
    
    // constructor
    < Parent >( int a, int b) {
        param1 = a; 
        param2 = b;
    }
}

// < Child >.java
public class < Child > extends < Parent > {
    
    // constructor
    < Child >( int param1, int param2) {
		super(param1, param2); // calls parent constructor
    }
}

Reflection

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide, resources linked in SDP week 4 pre-lecture material

The concept of reflection in software means the ability to inspect, analyze and modify other code at runtime.

Classes

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide

From a class object we can retrieve all kind of information like declared methods, constructors, visible fields, annotations, types.

In order to retrieve the class information from a single instance we can write (in this case, for the String class):

Class<String> stringclass = String.class;

or using the java.lang.Class.forName(String) method:

Class.forName( "java.lang.String" )

It is also possible to create new instances of a given class using the method java.lang.Class.newInstance() passing the right arguments:

String newInstanceStringClass = stringclass.newInstance();
String otherInstance = (String)Class.forName( "java.lang.String" ).newInstance();

The java.lang.Class.newInstance() method can be used only when the class contains a public default constructor or a constructor without arguments.

Interfaces

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide

Interfaces can be accessed like a class using their qualified name. All methods available for classes are available for interfaces as well.

Assuming that the InterfaceExample element is an interface.

System.out.println( "interface name: " + InterfaceExample.class.getName() );

One obvious difference between classes and interfaces is that interfaces cannot be instantiated using reflection via the newInstance() method.

Primitive types

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide

Primitive types like int, float, double, etc. can be accessed and used almost like any other classes.

It is possible to retrieve a class object from a primitive type as for any other non primitive type:

Class<Integer> intClass = int.class;

But it is not possible to create new instances for primitive types using reflection.

It is possible to check if a given class belongs to a primitive type or not using the method java.lang.Class.isPrimitive()

System.out.println( "is primitive: " + intClass.isPrimitive() );

Fields

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide

Classes offer several methods to access their fields at runtime. The most important ones are:

  • java.lang.Class.getDeclaredFields(): It returns an array with all declared fields for the class. It returns all private fields as well.
  • java.lang.Class.getFields(): It returns an array with all accessible fields for the class.
  • java.lang.Class.getField(String): It returns a field with the name passed as parameter. It throws an exception if the field does not exist or is not accessible.
  • java.lang.Class.getDeclaredField(String name): It returns a field with the given name, if the field does not exist it throws an exception.

These methods return an array of elements (or a single one) of the type java.lang.reflect.Field. This class contains several interesting methods that can be used at runtime that allow a programmer to read the properties and the values of the specific field.

Example use case:

Class<String> stringclass = String.class;

Field[] fields = stringclass.getDeclaredFields();

for( Field field : fields ) {
    System.out.println( "*************************" );
    System.out.println( "Name: " + field.getName() );
    System.out.println( "Type: " + field.getType() );

    if( field.isAccessible() ) {
        System.out.println( "Get: " + field.get( stringer ) );
    }

    System.out.println( "Modifiers:" + field.getModifiers() );
    System.out.println( "isAccesible: " + field.isAccessible() );
}

Field fieldHashCode = stringclass.getDeclaredField( "hash" );
fieldHashCode.setAccessible( true );

Object value = fieldHashCode.get( stringer );
int valueInt = fieldHashCode.getInt( stringer );
System.out.println( value );
System.out.println( valueInt );

The output or the program would be:

*************************

Name: value

Type: class [C

Modifiers:18

isAccesible: false

*************************

Name: hash

Type: int

Modifiers:2

isAccesible: false

*************************

Name: serialVersionUID

Type: long

Modifiers:26

isAccesible: false

*************************

Name: serialPersistentFields

Type: class [Ljava.io.ObjectStreamField;

Modifiers:26

isAccesible: false

*************************

Name: CASE_INSENSITIVE_ORDER

Type: interface java.util.Comparator

Modifiers:25

isAccesible: false

0

0

Methods

🔗 Source: Java Reflection Tutorial – The ULTIMATE Guide

Using the method java.lang.Class.getMethods() all visible or accessible methods for a given class are retrieved. In order to retrieve all visible methods for a given class we can do the following:

Class<String> stringclass = String.class;

Method[] methods = stringclass.getMethods();

We can also retrieve an specific method using its name and the type of the arguments it is expecting to receive:

Class<String> stringclass = String.class;

Method methodIndexOf = stringclass.getMethod( "indexOf", String.class );

For a given method (an instance of the type java.lang.reflect.Method), we can access all its properties. The following snippet shows a couple of them like name, default values, return type, modifiers, parameters, parameter types or the exceptions thrown, we can also check if a method is accessible or not:

for( Method method : methods ) {

    System.out.println( "****************************************************" );
    System.out.println( "name: " + method.getName() );
    System.out.println( "defaultValue: " + method.getDefaultValue() );
    System.out.println( "generic return type: " + method.getGenericReturnType() );
    System.out.println( "return type: " + method.getReturnType() );
    System.out.println( "modifiers: " + method.getModifiers() );

    Parameter[] parameters = method.getParameters();
    System.out.println( parameters.length + " parameters:" );

    for( Parameter parameter : parameters ) {
        System.out.println( "parameter name: " + parameter.getName() );
        System.out.println( "parameter type: " + parameter.getType() );
    }

    Class<?>[] parameterTypes = method.getParameterTypes();
    System.out.println( parameterTypes.length + " parameters:" );

    for( Class<?> parameterType : parameterTypes ) {
        System.out.println( "parameter type name: " + parameterType.getName() );
    }

    Class<?>[] exceptionTypes = method.getExceptionTypes();
    System.out.println( exceptionTypes.length + " exception types: " );

    for( Class<?> exceptionType : exceptionTypes ) {
        System.out.println( "exception name " + exceptionType.getName() );
    }

    System.out.println( "is accesible: " + method.isAccessible() );
    System.out.println( "is varArgs: " + method.isVarArgs() );
}

It is also possible to instantiate given methods for specific objects passing the arguments that we want, we should assure that the amount and type of the arguments is correct:

Object indexOf = methodIndexOf.invoke( stringer, "called" );

This last feature is very interesting when we want to execute specific methods at runtime under special circumstances.

Package: java.lang

Provides classes that are fundamental to the design of the Java programming language:

  • The most important classes are Object, which is the root of the class hierarchy, and
  • Class, instances of which represent classes at run time.

The class Math provides commonly used mathematical functions such as sine, cosine, and square root. The classes String, StringBuffer, and StringBuilder similarly provide commonly used operations on character strings.

Class Throwable encompasses objects that may be thrown by the throw statement. Subclasses of Throwable represent errors and exceptions.

N.B: The package java.lang.reflect provides classes and interfaces for obtaining reflective information about classes and objects.

public final class Array
extends Object

The Array class provides static methods to dynamically create and access Java arrays.

<type>[] <name> = new <type>[<size>]

In Java, all arrays are dynamically allocated.

/* In one step: 
 * <type>[] <name> = new <type>[<size>];
*/
int[] arr = new int[20];
int[][] arr2D = new int[10][20]; //a 2D array or matrix
int[][][] arr3D = new int[10][20][10]; //a 3D array

/* 
 * In two steps: 
 * <type>[] <name>;
 * <name> = new <type>[<size>];
*/
int[] arr;
arr = new int[20];

/*
 * As array literal: 
 * <type>[] <name> = new <type>[]{ element, element, element, ... }; 
*/
int[] arr = new int[]{ 1,2,3,4,5,6,7,8,9,10 }; 
String[] suits = {  "clubs", "diamond", "hearts", "spades" };

// arr[i][j] = arr[👇][👉]
int[][] arr = {
  { 00, 01, 02, 03, },
  { 10, 11, 12, 13, },
  { 20, 21, 22, 23, },
  { 30, 31, 32, 33, },
};

jshell> arr[0][0] ==> 0
jshell> arr[0][1] ==> 1
jshell> arr[1][0] ==> 10
jshell> arr[1][2] ==> 12
jshell> arr[3][0] ==> 30

Source: Geekforgeeks

arr.length

Since arrays are objects in Java, we can find their length using the object property length. Instead of s.length(), which is a String method that computers the length of a String.

Source: Geekforgeeks

For detail see: obj.clone() in this doc.

Deep copy and shallow copy:

⚠️ Note: Java does not support deep copying by fault. You have to manually implement code to deep copy an object.

When you clone a single-dimensional array, such as Object[], a “deep copy” is performed with the new array containing copies of the original array’s elements as opposed to references.

A clone of a multi-dimensional array (like Object[][]) is a “shallow copy,” however, which is to say that it creates only a single new array with each element array a reference to an original element array, but subarrays are shared.

Source: Geekforgeeks

Context:

  • Every array type implements the interface Cloneable (Source: Geekforgeeks).
  • Since the Array class extends the Object class (see class signature below), it inherits the obj.clone() method defined in the Object class.
public final class Array
extends Object
public class Object
  • Class Object is the root of the class hierarchy.
  • Every class has Object as a superclass.
  • All objects, including arrays, implement the methods of this class.
protected Object clone()
                throws CloneNotSupportedException

Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.

The general intent is that, for any object x, the expression:

  • x.clone() != x will be true,
  • x.clone().getClass() == x.getClass() will be true, and
  • x.clone().equals(x) will be true.

N.B: ⚠️ But these are not absolute requirements! ⚠️

public boolean equals(Object obj)

Indicates whether some other object is "equal to" this one, true/false).

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.
@Override // overrides Object equals() method (for my own objects definition)
public boolean equals(Object obj) {
        // reflexivity
        if(this == obj) { return true; } 
        // comparing apples and pears
        if((obj == null) || (obj.getClass() != this.getClass())) { return false; }
        // Can now safely cast Pair type onto obj
        Pair otherPair = (Pair) obj;
        // equal is x and y are equal
        return x == otherPair.get_x() && y == otherPair.get_y();
}

@Override 
public int hashCode() { 
    int hash = 7;
    return x * hash - y * hash;
}

When we override equals(), it is recommended to also override the hashCode() method. If we don’t do so, equal objects may get different hash-values; and hash based collections, including HashMap, HashSet, and Hashtable do not work properly.

Source: Geekforgeeks

@Override 
public int hashCode() { 
    int hash = 7;
    return x * hash - y * hash;
}
public final Class<?> getClass()

Returns the runtime class of this Object.

public String toString()

Returns a string representation of the object.

In general, the toString method returns a string that "textually represents" this object:

  • The result should be a concise but informative representation that is easy for a person to read.
  • It is recommended that all subclasses override this method.

The Character class wraps a value of the primitive type char in an object. An object of class Character contains a single field whose type is char.

Two parameter types:

  • int codepoint (unicode)
  • char ch (character)
public static boolean isAlphabetic(int codePoint)

Mostly used with s.codePointAt(i) e.g.

Character.isAlphabetic(s.codePointAt(i))
public static boolean isLetter(char ch)

A character is considered to be a letter if its general category type, provided by Character.getType(ch), is any of the following:

  • UPPERCASE_LETTER
  • LOWERCASE_LETTER
  • TITLECASE_LETTER
  • MODIFIER_LETTER
  • OTHER_LETTER
public static boolean isDigit(char ch)

A character is a digit if its general category type, provided by Character.getType(ch), is DECIMAL_DIGIT_NUMBER.

public static boolean isLowerCase(char ch)

A character is lowercase if its general category type, provided by Character.getType(ch), is LOWERCASE_LETTER

public static boolean isUpperCase(char ch)

A character is uppercase if its general category type, provided by Character.getType(ch), is UPPERCASE_LETTER.

public static boolean isWhitespace(char ch)

A character is a Java whitespace character if and only if it satisfies one of the following criteria: It is a Unicode space character (SPACE_SEPARATOR, LINE_SEPARATOR, or PARAGRAPH_SEPARATOR) but is not also a non-breaking space ('\u00A0', '\u2007', '\u202F').

  • It is '\t', U+0009 HORIZONTAL TABULATION.
  • It is '\n', U+000A LINE FEED.
  • It is '\u000B', U+000B VERTICAL TABULATION.
  • It is '\f', U+000C FORM FEED.
  • It is '\r', U+000D CARRIAGE RETURN.
  • It is '\u001C', U+001C FILE SEPARATOR.
  • It is '\u001D', U+001D GROUP SEPARATOR.
  • It is '\u001E', U+001E RECORD SEPARATOR.
  • It is '\u001F', U+001F UNIT SEPARATOR.
public static char toLowerCase(char ch)

Converts the character argument to lowercase using case mapping information from the UnicodeData file.

public static char toUpperCase(char ch)

Converts the character argument to uppercase using case mapping information from the UnicodeData file.

The String class represents character strings. All string literals in Java programs, such as "abc", are implemented as instances of this class.

Strings are constant (immutable); their values cannot be changed after they are created.

public char charAt(int index)

Returns the length of this string. The length is equal to the number of Unicode code units in the string.

public char charAt(int index)

Returns the char value at the specified index. An index ranges from 0 to length() - 1.

public int indexOf(int ch)

Returns the index within this string of the first occurrence of the specified character ch.

public int indexOf(int ch,
                   int fromIndex)

Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index.

public int codePointAt(int index)       

Returns the character (Unicode code point) at the specified index. The index refers to char values (Unicode code units) and ranges from 0 to length() - 1.

public String toLowerCase()

Converts all of the characters in this String to lower case using the rules of the default locale.

public String toUpperCase()

Converts all of the characters in this String to upper case using the rules of the default locale.

public int compareTo(String anotherString)

Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings.

  • negative integer: if s lexicographically precedes the argument string s2, e.g. pseudo-code a.compareTo( z ) < 1
  • positive integer: if s lexicographically follows the argument string s2, e.g. pseudo-code z.compareTo( a ) > 1
  • 0: when the s.equals(s2) returns true, , e.g. pseudo-code a.compareTo( a ) == 1

Compares two strings lexicographically, ignoring case differences. This method returns an integer whose sign is that of calling compareTo with case folded versions of the strings where case differences have been eliminated by calling Character.toLowerCase(Character.toUpperCase(int)) on each Unicode code point.

String[] split(String regex, int limit)

Use regex101 to test matches.

String entireLine = "Joey ate an apple; then,";f
entireLine.split( "[^a-zA-Z]" ); // { "Joey", "ate", "an", "apple", "", "then" }

Splits this string around matches of the given regular expression. The array returned by this method contains each substring of this string that is terminated by another substring that matches the given expression or is terminated by the end of the string. The substrings in the array are in the order in which they occur in this string. If the expression does not match any part of the input then the resulting array has just one element, namely this string.

The limit parameter controls the number of times the pattern is applied and therefore affects the length of the resulting array.

  • If the limit is positive then the pattern will be applied at most limit - 1 times, the array's length will be no greater than limit, and the array's last entry will contain all input beyond the last matched delimiter.

  • If the limit is zero then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded.

  • If the limit is negative then the pattern will be applied as many times as possible and the array can have any length.

The string "boo:and:foo", for example, yields the following results with these parameters:

Regex Limit Result
: 2 { "boo", "and:foo" }
: 5 { "boo", "and", "foo" }
: -2 { "boo", "and", "foo" }
o 5 { "b", "", ":and:f", "", "" }
o -2 { "b", "", ":and:f", "", "" }
o 0 { "b", "", ":and:f" }
String[] split(String regex)

Splits this string around matches of the given regular expression.

This method works as if by invoking the two-argument split method with the given expression and a limit argument of zero.

Trailing empty strings are therefore not included in the resulting array.

The string "boo:and:foo", for example, yields the following results with these expressions:

Regex Result
: { "boo", "and", "foo" }
o { "b", "", ":and:f" }
public final class StringBuilder
extends Object
implements Serializable, Comparable<StringBuilder>, CharSequence

A mutable sequence of characters. The principal operations on a StringBuilder are the append (at end) and insert (at index) methods, which are overloaded so as to accept data of any type.

If sb refers to an instance of a StringBuilder, then sb.append(x) has the same effect as sb.insert(sb.length(), x).

Use sb.toString() to convert the object into an immutable String.

Constructs a string builder initialized to the contents of the specified string.

StringBuilder sbEmpty = new StringBuilder();
StringBuilder sbExisting = new StringBuilder("Hello");

Returns a string representing the data in this sequence.

Appends the string representation of the Object argument.

Exists with various methods signatures:

  • append(boolean b): Appends the string representation of the boolean argument to the sequence.
  • append(char c): Appends the string representation of the char argument to this sequence.
  • append(double d): Appends the string representation of the double argument to this sequence.
  • append(float f): Appends the string representation of the float argument to this sequence.
  • append(int i): Appends the string representation of the int argument to this sequence.
  • append(long lng): Appends the string representation of the long argument to this sequence.
  • append(Object obj): Appends the string representation of the Object argument.
  • append(String str): Appends the specified string to this character sequence.

Removes the characters in a substring of this sequence. The substring begins at the specified start and extends to the character at index end - 1 or to the end of the sequence if no such character exists. If start is equal to end, no changes are made.

public StringBuilder deleteCharAt(int index)

Removes the char at the specified position in this sequence. This sequence is shortened by one char.

public StringBuilder replace​(int start, int end, String str)

Parameters:

  • start - The beginning index, inclusive.
  • end - The ending index, exclusive.
  • str - String that will replace previous contents.
public class Thread
extends Object
implements Runnable

A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

There are two ways to create a new thread of execution. [Our] way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.

The Runnable interface defines one method: public void run() The Thread class defines a number of methods, e.g.:

  • constructors: Thread(Runnable job), Thread(Runnable job, String name)
  • starts a thread: void start()
  • reports whether the thread is running: final boolean isAlive()
  • suspends a thread for the given duration: static void sleep(long milliseconds)

From POP2 lecture:

We’ll use the Thread class & the Runnable interface to create and start threads.

Our recipe:

  1. Create a class that implements Runnable. Runnable defines only one method: public void run()
  2. Create an instance of the class (“a job”)
  3. Create an instance of Thread, passing it the instance from 2
  4. Start the Thread instance!

We can think of a thread (an instance of Thread) as a worker; for it to be useful, we need to give it a job!

Example:

// TestRunnable.java

public class TestRunnable implements Runnable {

    // defines the class that implements the run() method of the `Runnable` interface
    public void run() {
      /*
        try
        {  Thread.sleep(1000);  }
        catch( InterruptedException e )
        {  e.printStackTrace();  }
      */

        printsmth();
    }

    void printsmth() {
        System.out.println( "I'm active." );
    }

// try this both and without sleeping
}
// MainTestRunnable.java

public class MainTestRunnable {
    // example 1
    public static void main(String[] args) {
        System.out.println("Hello World, from main()!");

        // an instance of the class (that implements Runnable) we defined above
        Runnable job = new TestRunnable();
        // an instance of `Thread` to which we pass the instance of the class above
        Thread thread = new Thread(job);

        // starts the `Thread` instance
        thread.start();

        System.out.println( "Finishing in main()." );
    }
}

Source: POP2, credit to Hubie Chen

synchronized methods

In a class that implement Runnable, we can declare a method as synchronized to synchronize different threads' access to it. If we have an instance of Thread with an instance of that class as an argument:

  • only 1 thread can be in the synchronized method at a time
  • (in fact) only 1 thread can be in any synchronized method (of the instance) at a time

💡 Note: We can only synchronize with methods if we wrote the method ourselves. If we use a library method, we don't want to modify the library's method declaration. In that case, we can use synchronized blocks (see next).

// Counter.java
public class Counter implements Runnable {
    // attributes
    int start;
    int end;

    // constructor
    Counter( int start, int end ) {
        this.start = start;
        this.end = end;
    }

    // implements run()
    synchronized public void run() {
        
        // simply prints counter from 1 to n
        for (int i = this.start; i <= this.end; i++) {
            System.out.println(
                Thread.currentThread().getName()
                + " i: " 
                + i);
        }
    }
}
public class MainCounter {

    public static void main(String[] args) {
        
        // create instances of Counter
        Counter counterA = new Counter( 1, 10 );

        // creates threads
        Thread threadA1 = new Thread( counterA );
        Thread threadA2 = new Thread( counterA );

        // starts the threads
        threadA1.start();
        threadA2.start();
    }   
}
// Output without `synchronized void run()`
Thread-0 i: 1
Thread-1 i: 1
Thread-0 i: 2
Thread-1 i: 2
Thread-0 i: 3
Thread-1 i: 3
Thread-0 i: 4
Thread-0 i: 5
Thread-0 i: 6
Thread-1 i: 4
Thread-0 i: 7
Thread-1 i: 5
Thread-1 i: 6
Thread-1 i: 7
Thread-1 i: 8
Thread-1 i: 9
Thread-0 i: 8
Thread-0 i: 9
Thread-0 i: 10
Thread-1 i: 10
// Output **with** `synchronized void run()`
Thread-0 i: 1
Thread-0 i: 2
Thread-0 i: 3
Thread-0 i: 4
Thread-0 i: 5
Thread-0 i: 6
Thread-0 i: 7
Thread-0 i: 8
Thread-0 i: 9
Thread-0 i: 10
Thread-1 i: 1
Thread-1 i: 2
Thread-1 i: 3
Thread-1 i: 4
Thread-1 i: 5
Thread-1 i: 6
Thread-1 i: 7
Thread-1 i: 8
Thread-1 i: 9
Thread-1 i: 10

synchronized ( object ) block

We can only use synchronize with methods if we wrote the method ourselves. If we use a library method, we can't modify the library's method declaration. In that case, we can use synchronized blocks when we call that method.

Example:

synchronized( object ) {
    // code that is synchronized
    // ... 
}

Using synchronized blocks allows us to synchronize part of a method

synchronized void someMethodInAClass() {
        // synchronized code here 
    }

// ...is equivalent to...

void someMethodInAClass() {
    synchronized( this ) {
        // code here 
    }
}

```java
// Counter.java
public class Counter implements Runnable {
    // attributes
    int start;
    int end;

    // constructor
    Counter( int start, int end ) {
        this.start = start;
        this.end = end;
    }

    // implements run()
    public void run() {
        
        synchronized ( this ) {
            // simply prints counter from 1 to n
            for (int i = this.start; i <= this.end; i++) {
                System.out.println(
                    Thread.currentThread().getName()
                    + " i: " 
                    + i);
            }
        }
    }
}
// MainCounter.java
public class MainCounter {

    public static void main(String[] args) {
        
        // create instances of Counter
        Counter counterA = new Counter( 1, 10 );

        // creates threads
        Thread threadA1 = new Thread( counterA );
        Thread threadA2 = new Thread( counterA );

        // starts the threads
        threadA1.start();
        threadA2.start();
    }   
}
// Output without `synchronized ( this ) { ... }`
Thread-0 i: 1
Thread-1 i: 1
Thread-0 i: 2
Thread-1 i: 2
Thread-1 i: 3
Thread-1 i: 4
Thread-0 i: 3
Thread-0 i: 4
Thread-0 i: 5
Thread-0 i: 6
Thread-1 i: 5
Thread-1 i: 6
Thread-1 i: 7
Thread-1 i: 8
Thread-1 i: 9
Thread-1 i: 10
Thread-0 i: 7
Thread-0 i: 8
Thread-0 i: 9
Thread-0 i: 10
// Output **with** `synchronized ( this ) { ... }`
Thread-0 i: 1
Thread-0 i: 2
Thread-0 i: 3
Thread-0 i: 4
Thread-0 i: 5
Thread-0 i: 6
Thread-0 i: 7
Thread-0 i: 8
Thread-0 i: 9
Thread-0 i: 10
Thread-1 i: 1
Thread-1 i: 2
Thread-1 i: 3
Thread-1 i: 4
Thread-1 i: 5
Thread-1 i: 6
Thread-1 i: 7
Thread-1 i: 8
Thread-1 i: 9
Thread-1 i: 10
public final boolean isAlive()

Tests if this thread is alive. A thread is alive if it has been started and has not yet died.

Returns:

  • true if this thread is alive; false otherwise.

From POP2:

This method returns true if the thread (on which it’s called) is still running, and false otherwise

Causes the current thread to wait until it is awakened, typically by being notified or interrupted.

From POP2:

waits until another thread invokes a notify method

Causes the current thread to wait until it is awakened, typically by being notified or interrupted, or until a certain amount of real time has elapsed.

From POP2:

waits until notified or the given amount of time (in milliseconds) has passed

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.

From POP2:

Call to notify() awakens one waiting thread. Doesn’t give up caller’s lock (but after caller gives up lock, another thread can take it)

Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods.

From POP2:

Call to notifyAll() awakens all threads – can be used when potentially many threads are waiting for a task to finish

thread1.join() basically means that subsequent threads that wants to start (thread2.start()) have to wait for thread1 to complete. Meanwhile, the code execution doesn't stop at .join() but the threads are made to wait.

This is illustrated with the below 5 second delay counter in each thread.

// JoinExample.java
public class JoinExample implements Runnable {
    // attributes
    int sec; // number of loop repetitions and thus seconds

    // constructor
    JoinExample( int sec ) {
        this.sec = sec;
    }

    // implements run() from Runnable
    public void run() {
        for (int i = 1; i <= this.sec; i++) { // repeat 5 times (i.e. delay 5 seconds)
            try {
                // make Thread wait for 1 second during each iteration of the loop
                Thread.sleep(1000); // 1000ms = 1sec
                
                // print current number of the iteration to console
                System.out.println( 
                    Thread.currentThread().getName()
                    + ": "
                    + i
                    + " second(s) " 
                );
            } catch (Exception e) {
                System.out.println(
                    "Exception has been caught" 
                    + e);
            }
        }
    }
}
// MainJoinExample.java
public class MainJoinExample {
    public static void main(String[] args) {
        
        // create JoinExample instance
        JoinExample secondsDelayer = new JoinExample( 5 ); // 5 second delay

        // create Thread instances with JoinExample (each will count 5 seconds)
        Thread thread1 = new Thread( secondsDelayer );
        Thread thread2 = new Thread( secondsDelayer );
        Thread thread3 = new Thread( secondsDelayer );

        // start first thread
        thread1.start();

        try {
            thread1.join();
            System.out.println("Thread just finished");
        } catch (Exception e) {
            System.out.println(
                "Exception has been caught" 
                + e
            );
        }

        // start second thread
        thread2.start();

        try {
            thread2.join();
            System.out.println("Thread just finished");
        } catch (Exception e) {
            System.out.println(
                "Exception has been caught" 
                + e
            );
        }

        // start third thread
        thread3.start();

        try {
            thread3.join();
            System.out.println("Thread just finished");
        } catch (Exception e) {
            System.out.println(
                "Exception has been caught" 
                + e
            );
        }
    }
}
// with `join()`
Thread-0: 1 second(s) 
Thread-0: 2 second(s) 
Thread-0: 3 second(s) 
Thread-0: 4 second(s) 
Thread-0: 5 second(s) 
Thread just finished
Thread-1: 1 second(s) 
Thread-1: 2 second(s) 
Thread-1: 3 second(s) 
Thread-1: 4 second(s) 
Thread-1: 5 second(s) 
Thread just finished
Thread-2: 1 second(s) 
Thread-2: 2 second(s) 
Thread-2: 3 second(s) 
Thread-2: 4 second(s) 
Thread-2: 5 second(s) 
Thread just finished
// without `.join()`
Thread just finished
Thread just finished
Thread just finished
Thread-1: 1 second(s) 
Thread-0: 1 second(s) 
Thread-2: 1 second(s) 
Thread-0: 2 second(s) 
Thread-1: 2 second(s) 
Thread-2: 2 second(s) 
Thread-0: 3 second(s) 
Thread-2: 3 second(s) 
Thread-1: 3 second(s) 
Thread-0: 4 second(s) 
Thread-2: 4 second(s) 
Thread-1: 4 second(s) 
Thread-1: 5 second(s) 
Thread-2: 5 second(s) 
Thread-0: 5 second(s) 

Thrown to indicate that the requested operation is not supported.

Example:

public class Penguin extends Bird implements SwimmingAnimal, MoltingAnimal {
    // ...

    public void fly() {
        throw new UnsupportedOperationException();
    }
}
public class PenguinTest {
    //...

    @Test
    public void testItCantActuallyFly() {
        Penguin penguin = new Penguin(5);
        assertThrows(UnsupportedOperationException.class, penguin::fly);
    }
}

Source: SDP > worksheet 1

Package: java.lang.reflect

Package summary: Package java.lang.reflect

Provides classes and interfaces for obtaining reflective information about classes and objects. Reflection allows programmatic access to information about the fields, methods, and constructors of loaded classes

Classes in this package, along with java.lang.Class accommodate applications such as debuggers, interpreters, object inspectors, class browsers, and services such as Object Serialization and JavaBeans that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class.

🔗 Source: Package summary: Package java.lang.reflect

The Reflection API allows a Java program to inspect and manipulate itself; it comprises the java.lang.Class class and the java.lang.reflect package, which represents the members of a class with Method, Constructor, and Field objects.

🔗 Source: BBK > SDP > week 4 worksheet

Package: java.io

Reads text from character files using a default buffer size. The FileReader is meant for reading streams of characters. For reading streams of raw bytes, consider using a FileInputStream.

We use the FileReader class, which extends InputStreamReader which extends Reader

Commonly used contructor:

FileReader( String filename ) throws FileNotFoundException

We often use a FileReader wrapped in a BufferedReader to read more efficiently.

Reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines. The buffer size may be specified, or the default size may be used. The default is large enough for most purposes.

In general, each read request made of a Reader causes a corresponding read request to be made of the underlying character or byte stream. It is therefore advisable to wrap a BufferedReader around any Reader whose read() operations may be costly, such as FileReaders and InputStreamReaders. For example,

 BufferedReader in = new BufferedReader(new FileReader("foo.in"));

will buffer the input from the specified file. Without buffering, each invocation of read() or readLine() could cause bytes to be read from the file, converted into characters, and then returned, which can be very inefficient.

Use String[] split(String regex, int limit) to split entireLine into a String[] with each matching word.

String entireLine;
try( BufferedReader br = new BufferedReader( new FileReader( "test.txt" ))) {
    while(( entireLine = br.readLine()) != null ) {
      // do something with entireLine read
    }
} catch( IOException exc ) {
    System.out.println( "I/O Error: " + exc );
}

Example from POP2:

public static void main(String[] args) {
  String s;
  try( BufferedReader br = new BufferedReader( new FileReader( "test.txt" ))) {
    int lineNr = 0;
    while(( entireLine = br.readLine()) != null ) {
        System.out.println( "" + lineNr + ": " + entireLine );
        lineNr++;
    }
  } catch( IOException exc ) {
    System.out.println( "I/O Error: " + exc );
  }
}

Source: POP2, credit to Hubie Chen (08-code-io)

public int read() throws IOException

Reads a single character.

Overrides: read in class Reader

Returns: The character read, as an integer in the range 0 to 65535 (0x00-0xffff), or -1 if the end of the stream has been reached

public String readLine() throws IOException

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), a carriage return followed immediately by a line feed, or by reaching the end-of-file (EOF).

Returns: A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached without reading any characters

public void close() throws IOException

Closes the stream and releases any system resources associated with it. Once the stream has been closed, further read(), ready(), mark(), reset(), or skip() invocations will throw an IOException. Closing a previously closed stream has no effect.

We use the FileWriter class which extends class OutputStreamWriter which extends abstract class Writer.

Two commonly used constructors:

FileWriter( String filename )
FileWriter( String filename, boolean append )

append set to true --> output appended to end of file append set to false --> file overwritten

try( FileWriter fw = new FileWriter( "test.txt" )) {
  // write to file
} catch( IOException exc ) {
        System.out.println( "I/O error: " + exc );
}

Example:

public static void main(String[] args) {
    String str;
    BufferedReader br = new BufferedReader( new InputStreamReader( System.in ));
    System.out.println( "Enter text; 'stop' to quit." );

    try( FileWriter fw = new FileWriter( "test.txt" ))
    {
        do {
            str = br.readLine();
            if( str.equals( "stop" )) break;
            str = str + "\n";
            fw.write( str );
        } while( true );
    }
    catch( IOException exc )
    {
        System.out.println( "I/O error: " + exc );
    }
}

Source: POP2, credit to Hubie Chen (08-dist-io)

public void write(String str) throws IOException

Writes a string.

Throws: IOException - If an I/O error occurs

Writes a portion of a string.

Parameters:

  • str - A String
  • off - Offset from which to start writing characters
  • len - Number of characters to write

Package: java.util

Contains the collections framework, some internationalization support classes, a service loader, properties, random number generation, string parsing and scanning classes, base64 encoding and decoding, a bit array, and several miscellaneous utility classes.

Class: Arrays

This class contains various methods for manipulating arrays (such as sorting and searching). This class also contains a static factory that allows arrays to be viewed as lists.

The methods in this class all throw a NullPointerException, if the specified array reference is null, except where noted.

Returns a string representation of the contents of the specified array. The string representation consists of a list of the array's elements, enclosed in square brackets ("[]"). Adjacent elements are separated by the characters ", " (a comma followed by a space). Elements are converted to strings as by String.valueOf(int). Returns "null" if a is null.

Returns a string representation of the "deep contents" of the specified array. If the array contains other arrays as elements, the string representation contains their contents and so on. This method is designed for converting multidimensional arrays to strings.

Returns true if the two specified arrays of ints are equal to one another. Two arrays are considered equal if both arrays contain the same number of elements, and all corresponding pairs of elements in the two arrays are equal. In other words, two arrays are equal if they contain the same elements in the same order. Also, two array references are considered equal if both are null.

Parameters:

  • a - one array to be tested for equality
  • a2 - the other array to be tested for equality

Returns:

  • true if the two arrays are equal

Returns true if the two specified arrays are deeply equal to one another. Unlike the equals(Object[],Object[]) method, this method is appropriate for use with nested arrays of arbitrary depth.

The sorting algorithm is a Dual-Pivot Quicksort by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm offers O(n log(n)) performance on all data sets, and is typically faster than traditional (one-pivot) Quicksort implementations.

Searches the specified array of ints for the specified value using the binary search algorithm. The array must be sorted (as by the sort(int[]) method) prior to making this call. If it is not sorted, the results are undefined. If the array contains multiple elements with the specified value, there is no guarantee which one will be found.

The comparison is consistent with equals, more specifically the following holds for arrays a and b:

Arrays.equals(a, b) == (Arrays.compare(a, b) == 0)
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
  • <E> = the type of elements in this list

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. (This class is roughly equivalent to Vector, except that it is unsynchronized.)

Constructor: ArrayList()

ArrayList<String> lString = new ArrayList<String>()
ArrayList<Integer> lInteger = new ArrayList<Integer>()

Constructs an empty list with an initial capacity of ten.

Elements in an ArrayList are actually objects.

To use other types, such as int, you must specify an equivalent wrapper class: Integer. For other primitive types, use: Boolean for boolean, Character for char, Double for double, etc:

Source: W3School

Inserts the specified element at the specified position in this list.

⚠️ Shifts the element currently at that position (if any) and any subsequent elements to the right (adds one to their indices).

Parameters:

  • index - index at which the specified element is to be inserted
  • element - element to be inserted

Throws:

  • IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())

Appends the specified element to the end of this list.

Parameters:

  • e - element to be appended to this list

Returns:

  • true (as specified by Collection.add(E))

Appends all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection's Iterator. The behavior of this operation is undefined if the specified collection is modified while the operation is in progress.

Parameters:

  • c - collection containing elements to be added to this list

Returns:

  • true if this list changed as a result of the call

Inserts all of the elements in the specified collection into this list, starting at the specified position. Shifts the element currently at that position (if any) and any subsequent elements to the right (increases their indices). The new elements will appear in the list in the order that they are returned by the specified collection's iterator.

⚠️ Note: No .lenth() for ArrayList, use .size() instead.

public int size()

Returns the number of elements in this list.

Removes the first occurrence of the specified element from this list, if it is present. If the list does not contain the element, it is unchanged. More formally, removes the element with the lowest index i such that Objects.equals(o, get(i)) (if such an element exists). Returns true if this list contained the specified element (or equivalently, if this list changed as a result of the call).

Removes the element at the specified position in this list. Shifts any subsequent elements to the left (subtracts one from their indices).

Returns:

  • the element that was removed from the list

lst.removeIf(predicate)

See in Interface Colletion<E> method c.removeIf(Predicate<?superE>filter) in this doc.

Removes all of the elements from this list. The list will be empty after this call returns.

public int indexOf(Object o)

Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.

if ( ! lst.contains( str ) ) { 
  ... 
}
lst.remove( lst.get( lst.size() - 1 ) ); // remove last element in list

Replaces the element at the specified position in this list with the specified element.

ArrayList<String> lst = new ArrayList<String>(); // assume this is full of strings
lst.set( 2, "abc" ); // replaces string at index 2 with "abc"
public class Collections extends Object

This class consists exclusively of static methods that operate on or return collections.

Return type: void (operation on same object)

Example:

ArrayList<String> lst = new ArrayList<String>(); // assume this contains String elements
Collections.sort( lst ); // sorts the ArrayList alphabetically

Return type: void (operation on same object)

When you want to specify a comparator to sort by.

Example:

class Simpson implements Comparable<Simpson> {
    String name;

    Simpson(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Simpson simpson) {
        return this.name.compareTo(simpson.name);
    }
}

//
public class SimpsonSorting {

     public static void main(String... sortingWithList) {
        List<SimpsonCharacter> simpsons = new ArrayList<>();
        simpsons.add(new SimpsonCharacter("Homer "));
        simpsons.add(new SimpsonCharacter("Marge "));
        simpsons.add(new SimpsonCharacter("Bart "));
        simpsons.add(new SimpsonCharacter("Lisa "));

        Collections.sort(simpsons); // Now you can use sort because the comparator is defined
        simpsons.stream().map(s -> s.name).forEach(System.out::print);

        Collections.reverse(simpsons);
        simpsons.stream().forEach(System.out::print);
    }
}

Source: InfoWorld

Interface: Comparator<T>

Type Parameters:

  • T - the type of objects that may be compared by this comparator

Functional Interface:

  • This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order.

Parameters:

  • o1 - the first object to be compared.
  • o2 - the second object to be compared.

Returns:

  • a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

The implementor must ensure that signum(compare(x, y)) == -signum(compare(y, x)) for all x and y.

The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0.

It is generally the case, but not strictly required that (compare(x, y)==0) == (x.equals(y)).

Class: Scanner

A simple text scanner which can parse primitive types and strings using regular expressions.

A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods.

The default whitespace delimiter used by a scanner is as recognized by Character.isWhitespace().

  • Scanner(File source): Constructs a new Scanner that produces values scanned from the specified file.
  • Scanner(String source): Constructs a new Scanner that produces values scanned from the specified input stream.
  • Scanner(InputStream source): Constructs a new Scanner that produces values scanned from the specified string.
  • (more available)

Sets this scanner's delimiting pattern to a pattern constructed from the specified String.

String input = "1 fish 2 fish red fish blue fish";
Scanner sc = new Scanner(input).useDelimiter("\\s*fish\\s*");

Closes this scanner.

sc.hasNext() variations

  • sc.hasNextInt()
  • sc.hasNextLine()
  • sc.hasNext(String pattern): Returns true if the next token matches the pattern constructed from the specified string.

Finds and returns the next complete token from this scanner.

  • sc.next(String pattern): Returns the next token if it matches the pattern constructed from the specified string.
  • [sc.nextInt()]: Scans the next token of the input as an int.
  • sc.nextLine(): Advances this scanner past the current line and returns the input that was skipped.

Interface: Collection<E>

public interface Collection<E>
extends Iterable<E>

The root interface in the collection hierarchy.

Removes all of the elements of this collection that satisfy the given predicate.

For context, Interface Predicate<T> is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Interface: Map<K,V>

  • An object that maps keys to values.
  • A map cannot contain duplicate keys;
  • each key can map to at most one value.

Type Parameters:

  • K: the type of keys maintained by this map
  • V: the type of mapped values

This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.

public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
HashMap< String , Set<Integer> > map = new HashMap< String , Set<Integer> >();

This implementation provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets.

Useful methods:

  • m.get(Object key)
  • m.put(K key, V value)
  • m.size()
  • m.remove(Object key)
  • m.clear()
  • m.clone()
  • m.containsKey(Object key)
  • m.containsValue(Object value)
  • m.entrySet() returns Set<Map.Entry<K,V>>
  • m.keySet() returns Set<K>

For each Map.Entry pair in entrySet()

// HashMap<K, V> m = new HashMap();

for (Map.Entry<K, V> pair: m.entrySet()) {
    // pair.getKey()
    // pair.getValue();
}

Source: zetcode.com

Iterator over entrySet()

// HashMap<K, V> m = new HashMap();

Iterator< Map.Entry<K, V> > it = items.entrySet().iterator();

while (it.hasNext()) {
    Map.Entry<K, V> pair = it.next();
    // pair.getKey()
    // pair.getValue();
}

Source: zetcode.com

Interface: Set<E>

public interface Set<E>
extends Collection<E>

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.

All known implementing classes:

  • AbstractSet
  • HashSet
  • LinkedHashSet
  • TreeSet
  • more excl. for simplicity

Class: HashSet<E>

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable

This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.

Constructs a new, empty set.

Constructs a new set containing the elements in the specified collection c.

public boolean add(E e)

Adds the specified element to this set if it is not already present.

If this set already contains the element, the call leaves the set unchanged and returns false.

public void clear()

Removes all of the elements from this set. The set will be empty after this call returns.

public Object clone()

Returns a shallow copy of this HashSet instance: the elements themselves are not cloned.

public boolean contains(Object o)

Returns true if this set contains the specified element.

public boolean isEmpty()

Returns true if this set contains no elements.

public boolean remove(Object o)

Removes the specified element from this set if it is present.

Returns true if this set contained the element (or equivalently, if this set changed as a result of the call).

public int size()

Returns the number of elements in this set (its cardinality).

public Object[] toArray()

Returns an array containing all of the elements in this collection.

Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted. Functional interfaces can provide a target type in multiple contexts, such as assignment context, method invocation, or cast context:

// Assignment context
Predicate<String> p = String::isEmpty;

The interfaces in this package are annotated with FunctionalInterface. This annotation is not a requirement for the compiler to recognize an interface as a functional interface, but merely an aid to capture design intent and enlist the help of the compiler in identifying accidental violations of design intent.

Functional interfaces often represent abstract concepts like functions, actions, or predicates. In documenting functional interfaces, or referring to variables typed as functional interfaces, it is common to refer directly to those abstract concepts, for example using "this function" instead of "the function represented by this object".

There are several basic function shapes, including:

  • Function (unary function from T to R),
  • Consumer (unary function from T to void),
  • Predicate (unary function from T to boolean), and
  • Supplier (nullary function to R).

Function shapes have a natural arity based on how they are most commonly used. The basic shapes can be modified by an arity prefix to indicate a different arity, such as:

  • BiFunction (binary function from T and U to R).

There are additional derived function shapes which extend the basic function shapes, including:

  • UnaryOperator (extends Function), and
  • BinaryOperator (extends BiFunction).

Interface: UnaryOperator

defines method apply() which applies a unary operation to an object of type T and returns the result (of type T)

Interface: BinaryOperator

similar to previous interface, but for applying an operation to two objects

Interface: Consumer

defines method accept(); applies an operation to an object of type T

Interface: Predicate

defines method test() which applies an operation to an object of type T and returns a boolean value

Package: java.util.stream

Package summary: java.util.stream

💡 Great TLDR and explanation in the docs above ☝️

Package summary

Classes to support functional-style operations on streams of elements. The key abstraction introduced in this package is stream.

The classes Stream, IntStream, LongStream, and DoubleStream are streams over objects and the primitive int, long, and double types.

Streams differ from collections in several ways:

  • No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
  • Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
  • Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization
  • Possibly unbounded. While collections have a finite size, streams need not
  • Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.

Streams can be obtained in a number of ways. Some examples include:

  • From a Collection via the stream() and parallelStream() methods;
  • From an array via Arrays.stream(Object[]);
  • From static factory methods on the stream classes, such as Stream.of(Object[]), IntStream.range(int, int) or Stream.iterate(Object, UnaryOperator);
  • The lines of a file can be obtained from BufferedReader.lines();
  • Streams of file paths can be obtained from methods in Files;
  • Streams of random numbers can be obtained from Random.ints();

Stream operations and pipelines

Docs: package java.util.stream

Stream operations are divided into

  • intermediate operations, and
  • terminal operations, and are combined to form stream pipelines.

A stream pipeline consists of:

  • a source (such as a Collection, an array, a generator function, or an I/O channel);
  • intermediate operation(s) (zero or more) such as Stream.filter or Stream.map which return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.
  • a terminal operation such as Stream.forEach or Stream.reduce, such as Stream.forEach or IntStream.sum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream.

Intermediate operations are further divided into:

  • stateless operations, such as filter and map, which retain no state from previously seen element when processing a new element -- each element can be processed independently of operations on other elements.
  • stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.

Types of method references in streams

See in this doc: Method references (e.g. ClassName::methodName)

Example:

List<String> words = List.of("hi", "bat", "ear", "hello", "iguana",
        "beaver", "winterland", "elephant", "eye", "qi");
Stream<String> sl = words.stream();
sl.forEach( s -> System.out.println("  " + s) );
List<String> words = List.of("hi", "bat", "ear", "hello", "iguana",
        "beaver", "winterland", "elephant", "eye", "qi");
Stream<String> sl = words.stream();
sl.forEach( System.out::println );

JUnit

assert

import org.junit.Test;
import static org.junit.Assert.*;

import java.io.*;
import java.util.*;

public class dist03Test {    
    @Test
    public void testBetween() {
        assert Between.between(1, 1).equals("");
        assert Between.between(1, 2).equals("");
        assert Between.between(1, 3).equals("");
        assert ! Between.between(2, 4).equals("");

        // EDGE CASES
        assert Between.between(2, 4).equals("3");
        assert Between.between(2, 4).equals("3");
        
        // BASE CASES
        assert Between.between(3, 10).equals("5 7 9");
        assert Between.between(7, 1).equals("5 3");
    }

    @Test
    public void testAnagrams() {
        
        // BASE CASES
        assert Anagrams.anagrams( "roam", "roman") == 'n';
        assert Anagrams.anagrams( "The Shining", "Highest inn" ) == '!';
        assert Anagrams.anagrams( "William Shakespeare", "Willie makes phrases" ) == 's';
        assert Anagrams.anagrams( "ebb", "ebb-flow" ) == 'w';
    }

    @Test
    public void testHashMap() {
        // BASE CASES: 1
        Map<Character, Integer> mTest = new HashMap<Character, Integer>();
        mTest.put('a', 1);
        mTest.put('b', 2);
        Map<Character, Integer> m = Anagrams.countLettersInMap("bab");
        assert m.equals(mTest);
        
        // BASE CASES: 2
        mTest = new HashMap<Character, Integer>();
        mTest.put('b', 1);
        m = Anagrams.countLettersInMap("b");
        assert m.equals(mTest);

        // BASE CASES: 3
        mTest = new HashMap<Character, Integer>();
        mTest.put('b', 1);
        m = Anagrams.countLettersInMap("b!");
        assert m.equals(mTest);
        
        // EDGE CASES: 1 (empty string)
        mTest = new HashMap<Character, Integer>();
        m = Anagrams.countLettersInMap("");
        assert m.equals(mTest);

        // EDGE CASES: 1 (non alphabetic character)
        mTest = new HashMap<Character, Integer>();
        m = Anagrams.countLettersInMap(" ");
        assert m.equals(mTest);

        // EDGE CASES: 1 (non alphabetic character)
        mTest = new HashMap<Character, Integer>();
        m = Anagrams.countLettersInMap("!");
        assert m.equals(mTest);
    }


    @Test
    public void testRoman() {
        // From checker
        assert Roman.convert( 7 ).equals("VII");

        // From instructions
        assert Roman.convert( 314 ).equals( "CCCXIV" );
        assert Roman.convert( 3490 ).equals( "MMMCDXC" );
    }
}

assertThrows()

void someTest() {
	// test exception is thrown (uses lambda expression)
	assertThrows(yourException.class, 
		() -> whatEverShouldThrowAnException());

More formally:

@Test
void exceptionTesting() {
    MyException thrown = assertThrows(
           MyException.class,
           () -> myObject.doThing(),
           "Expected doThing() to throw, but it didn't"
    );

    assertTrue(thrown.getMessage().contains("Stuff"));
}

// version with longer code logic 
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
    assertThrows(NullPointerException.class,
            ()->{
            //do whatever you want to do here
            //ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
            });
}

Source: Stack Overflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment