Skip to content

Simon Exercise List

SubTask 1 Search for an element using lineal search.
SubTask 2 Search for an element using dichotomic search.
SubTask 3 Order a list using insertion sort.
SubTask 4 Order a list using selection sort.
SubTask 5 Random permutation of the elements of a list.
SubTask 6 Merge the elements, in order, of two ordered lists.

package Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * A utility class providing common algorithms for working with lists.
 * <p>
 * All methods are {@code static} so the class is not intended to be instantiated.
 * </p>
 */
public final class ListUtils {

    // Private constructor to prevent instantiation
    private ListUtils() {
        throw new AssertionError("Cannot instantiate utility class");
    }

    /* ===============================================================
     *  SubTask 1 – Linear Search
     * =============================================================== */

    /**
     * Searches the list linearly for the given {@code key}.
     *
     * Precondition:
     *   1. {@code list} is not {@code null}.
     *
     * Postcondition:
     *   1. Returns the index of the first occurrence of {@code key} if present; otherwise returns {@code -1}.
     *   2. {@code list} remains unmodified.
     *
     * @param list the list to search
     * @param key  the element to find
     * @param <T>  the type of elements in the list
     * @return the index of {@code key} in {@code list}, or {@code -1} if not found
     */
    public static <T> int linearSearch(List<T> list, T key) {
        if (list == null) {
            throw new IllegalArgumentException("List must not be null");
        }
        for (int i = 0; i < list.size(); i++) {
            if ((key == null && list.get(i) == null) || (key != null && key.equals(list.get(i)))) {
                return i;
            }
        }
        return -1;
    }

    /* ===============================================================
     *  SubTask 2 – Dichotomic (Binary) Search
     * =============================================================== */

    /**
     * Searches the sorted list for the given {@code key} using the binary search algorithm.
     *
     * Precondition:
     *   1. {@code list} is not {@code null} and is sorted in non-decreasing order.
     *
     * Postcondition:
     *   1. Returns the index of {@code key} if present; otherwise returns {@code -1}.
     *   2. {@code list} remains unmodified.
     *
     * @param list the <em>sorted</em> list to search
     * @param key  the element to find
     * @param <T>  the type of elements in the list; must be {@link Comparable}
     * @return the index of {@code key} in {@code list}, or {@code -1} if not found
     */
    public static <T extends Comparable<? super T>> int binarySearch(List<T> list, T key) {
        if (list == null) {
            throw new IllegalArgumentException("List must not be null");
        }
        int low = 0;
        int high = list.size() - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            T midVal = list.get(mid);
            int cmp = (key == null)
                    ? (midVal == null ? 0 : -1)
                    : (midVal == null ? 1 : key.compareTo(midVal));
            if (cmp == 0) {
                return mid;
            } else if (cmp < 0) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

    /* ===============================================================
     *  SubTask 3 – Insertion Sort
     * =============================================================== */

    /**
     * Sorts the given list in-place using the insertion sort algorithm.
     *
     * Precondition:
     *   1. {@code list} is not {@code null}.
     *
     * Postcondition:
     *   1. Upon completion, {@code list} is sorted in non-decreasing order.
     *
     * @param list the list to sort
     * @param <T>  the element type; must be {@link Comparable}
     */
    public static <T extends Comparable<? super T>> void insertionSort(List<T> list) {
        if (list == null) {
            throw new IllegalArgumentException("List must not be null");
        }
        for (int i = 1; i < list.size(); i++) {
            T key = list.get(i);
            int j = i - 1;
            // Shift larger elements to the right
            while (j >= 0 && (list.get(j) != null && key != null && list.get(j).compareTo(key) > 0
                    || list.get(j) != null && key == null)) {
                list.set(j + 1, list.get(j));
                j--;
            }
            list.set(j + 1, key);
        }
    }

    /* ===============================================================
     *  SubTask 4 – Selection Sort
     * =============================================================== */

    /**
     * Sorts the given list in-place using the selection sort algorithm.
     *
     * Precondition:
     *   1. {@code list} is not {@code null}.
     *
     * Postcondition:
     *   1. Upon completion, {@code list} is sorted in non-decreasing order.
     *
     * @param list the list to sort
     * @param <T>  the element type; must be {@link Comparable}
     */
    public static <T extends Comparable<? super T>> void selectionSort(List<T> list) {
        if (list == null) {
            throw new IllegalArgumentException("List must not be null");
        }
        int n = list.size();
        for (int i = 0; i < n - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                T current = list.get(j);
                T min = list.get(minIndex);
                if ((current != null && min == null) || (current != null && min != null && current.compareTo(min) < 0)) {
                    minIndex = j;
                }
            }
            if (minIndex != i) {
                Collections.swap(list, i, minIndex);
            }
        }
    }

    /* ===============================================================
     *  SubTask 5 – Random Permutation (Shuffle)
     * =============================================================== */

    /**
     * Generates a random permutation of the elements of the given list (in-place).
     *
     * Precondition:
     *   1. {@code list} is not {@code null}.
     *
     * Postcondition:
     *   1. After execution, {@code list} contains the same elements in a random order.
     *
     * @param list the list to shuffle
     * @param rnd  the random source; if {@code null}, a new {@link Random} will be used
     * @param <T>  the element type
     */
    public static <T> void shuffle(List<T> list, Random rnd) {
        if (list == null) {
            throw new IllegalArgumentException("List must not be null");
        }
        if (rnd == null) {
            rnd = new Random();
        }
        // Fisher–Yates shuffle
        for (int i = list.size() - 1; i > 0; i--) {
            int j = rnd.nextInt(i + 1);
            Collections.swap(list, i, j);
        }
    }

    /* ===============================================================
     *  SubTask 6 – Merge Two Ordered Lists
     * =============================================================== */

    /**
     * Merges two <em>sorted</em> lists into a new sorted list.
     *
     * Precondition:
     *   1. {@code a} and {@code b} are not {@code null} and are both sorted in non-decreasing order.
     *
     * Postcondition:
     *   1. Returns a new list containing all elements of {@code a} and {@code b} in sorted order.
     *   2. The original lists {@code a} and {@code b} remain unmodified.
     *
     * @param a   the first sorted list
     * @param b   the second sorted list
     * @param <T> the element type; must be {@link Comparable}
     * @return a new sorted list containing the merged elements
     */
    public static <T extends Comparable<? super T>> List<T> merge(List<T> a, List<T> b) {
        if (a == null || b == null) {
            throw new IllegalArgumentException("Input lists must not be null");
        }
        List<T> result = new ArrayList<>(a.size() + b.size());
        int i = 0, j = 0;
        while (i < a.size() && j < b.size()) {
            T elemA = a.get(i);
            T elemB = b.get(j);
            if ((elemA == null && elemB == null) || (elemA != null && elemB != null && elemA.compareTo(elemB) <= 0) || (elemA == null)) {
                result.add(elemA);
                i++;
            } else {
                result.add(elemB);
                j++;
            }
        }
        // Append remaining elements
        while (i < a.size()) {
            result.add(a.get(i++));
        }
        while (j < b.size()) {
            result.add(b.get(j++));
        }
        return result;
    }
} 

Implement predicate

package functions;

/**
* A predicate that checks if an integer is positive (> 0).
* @version 0.1
*/
public class Predicate1 implements Predicate<Integer> {

    @Override
    public boolean canApply(Integer source) {
        return source != null;
    }

    @Override
    public Boolean apply(Integer source) {
        assert source != null : "source cannot be null";
        return source > 0;
    }

    @Override
    public boolean equals(Object other) {
        return other instanceof Predicate1;
    }

    @Override
    public String toString() {
        return "Predicate1: isPositive";
    }

} 

Arraylist object array

package collections;

import functions.Action;
import functions.BinaryFunction;
import functions.Function;
import functions.Predicate;

/**
 * An array implementation of a list.
 * @see {@link collections.List}
 * @version 0.1
 */
public class ArrayList<T> extends AbstractList<T> {

    /**
     * Default initial capacity for the internal array.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Backing array that stores the elements of this list. Only the
     * first {@code size} positions are in use – positions beyond that
     * index may contain stale values (or {@code null}).
     */
    private Object[] elements;

    /**
     * Current number of elements stored in this list. Always
     * non-negative and never greater than {@code elements.length}.
     */
    private int size;

    /**
     * Builds an empty list with the default initial capacity.
     */
    public ArrayList() {
        this(DEFAULT_CAPACITY);
    }

    /**
     * Builds an empty list with a specific initial capacity.
     *
     * @param initialCapacity the initial capacity for the backing array
     * @throws IllegalArgumentException if {@code initialCapacity < 0}
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("initialCapacity must be >= 0");
        }
        this.elements = new Object[initialCapacity];
        this.size = 0;
    }

    /* =============================================================
     *  Helper methods
     * ============================================================= */

    /**
     * Ensures that the backing array can hold at least
     * {@code minCapacity} elements. If it cannot, a bigger array is
     * allocated and the existing elements are copied over.
     */
    private void ensureCapacity(int minCapacity) {
        if (minCapacity <= elements.length) {
            return;
        }
        int newCapacity = Math.max(elements.length * 2 + 1, minCapacity);
        Object[] newArr = new Object[newCapacity];
        System.arraycopy(elements, 0, newArr, 0, size);
        elements = newArr;
    }

    /**
     * Checks that an index is in the range {@code 0 ≤ index < size}.
     */
    private void checkElementIndex(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("index out of range");
        }
    }

    /**
     * Checks that an index is in the range {@code 0 ≤ index ≤ size}
     * (note the inclusive upper bound – suitable for insertion).
     */
    private void checkPositionIndex(int index) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("index out of range");
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public T get(int i) {
        checkElementIndex(i);
        return (T) elements[i];
    }

    @Override
    public void add(T elem) {
        add(elem, size); // append is just insert at the end
    }

    @Override
    public void add(T elem, int i) {
        checkPositionIndex(i);
        ensureCapacity(size + 1);
        // shift elements to the right
        int numMoved = size - i;
        if (numMoved > 0) {
            System.arraycopy(elements, i, elements, i + 1, numMoved);
        }
        elements[i] = elem;
        size++;
    }

    @Override
    public void remove(T elem) {
        for (int i = 0; i < size; i++) {
            if (java.util.Objects.equals(elements[i], elem)) {
                remove(i);
                return;
            }
        }
    }

    @Override
    public void remove(int i) {
        checkElementIndex(i);
        int numMoved = size - i - 1;
        if (numMoved > 0) {
            System.arraycopy(elements, i + 1, elements, i, numMoved);
        }
        elements[--size] = null; // clear to let GC do its work
    }

    @Override
    public <E> List<E> map(Function<? super T, ? extends E> function) {
        if (function == null) {
            throw new IllegalArgumentException("null argument");
        }
        ArrayList<E> result = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            result.add(function.apply(value));
        }
        return result;
    }

    @Override
    public boolean all(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            if (!predicate.test(value)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean any(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            if (predicate.test(value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public List<T> filter(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        ArrayList<T> result = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            if (predicate.test(value)) {
                result.add(value);
            }
        }
        return result;
    }

    @Override
    public void forEach(Action<? super T> action) {
        if (action == null) {
            throw new IllegalArgumentException("null argument");
        }
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            action.consume(value);
        }
    }

    @Override
    public T reduce(T identity, BinaryFunction<T, T, T> accumulator) {
        if (accumulator == null) {
            throw new IllegalArgumentException("null argument");
        }
        T result = identity;
        for (int i = 0; i < size; i++) {
            @SuppressWarnings("unchecked")
            T value = (T) elements[i];
            result = accumulator.apply(result, value);
        }
        return result;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    /* =============================================================
     *  Object overrides
     * ============================================================= */

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof List<?>)) {
            return false;
        }
        List<?> o = (List<?>) other;
        if (o.size() != this.size) {
            return false;
        }
        for (int i = 0; i < size; i++) {
            if (!java.util.Objects.equals(this.get(i), o.get(i))) {
                return false;
            }
        }
        return true;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (int i = 0; i < size; i++) {
            sb.append(String.valueOf(elements[i]));
            if (i < size - 1) {
                sb.append(',');
            }
        }
        sb.append(']');
        return sb.toString();
    }

}

Anonymous class and interface example

Step 1

Main.java

import java.util.LinkedList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        sort(numbers);
        System.out.println(numbers.toString());
    }

    public static <T extends Comparable<? super T>> void sort(List<T> values) {
        assert values != null : "values cannot be null";
        if (values.size() > 2) {
            for (int sorted = 0; sorted < values.size(); sorted++) {
                int indexOfMax = maximumIndexFrom(values, sorted);
                swap(values, sorted, indexOfMax);
            }
        }
    }

    private static <T extends Comparable<? super T>> int maximumIndexFrom(List<T> values, int from) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert from >= 0 && from < values.size() : "from must be a valid index";
        int maxIndex = from;
        T max = values.get(from);
        for (int i = from + 1; i < values.size(); i++) {
            T current = values.get(i);
            if (current.compareTo(max) > 0) {
                max = current;
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    private static <T> void swap(List<T> values, int a, int b) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert a >= 0 && a < values.size() : "a must be a valid index";
        assert b >= 0 && b < values.size() : "b must be a valid index";
        T valueAtA = values.get(a);
        T valueAtB = values.get(b);
        values.set(a, valueAtB);
        values.set(b, valueAtA);
    }


}

Step 2

Main.java

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        sort(numbers, new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return a - b;
            }
        });
        System.out.println(numbers.toString());
    }

    public static <T> void sort(List<T> values, Comparator<? super T> comparator) {
        assert values != null : "values cannot be null";
        if (values.size() > 2) {
            for (int sorted = 0; sorted < values.size(); sorted++) {
                int indexOfMax = maximumIndexFrom(values, sorted, comparator);
                swap(values, sorted, indexOfMax);
            }
        }
    }

    private static <T> int maximumIndexFrom(List<T> values, int from, Comparator<? super T> comparator) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert from >= 0 && from < values.size() : "from must be a valid index";
        int maxIndex = from;
        T max = values.get(from);
        for (int i = from + 1; i < values.size(); i++) {
            T current = values.get(i);
            if (comparator.compare(current, max) > 0) {
                max = current;
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    private static <T> void swap(List<T> values, int a, int b) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert a >= 0 && a < values.size() : "a must be a valid index";
        assert b >= 0 && b < values.size() : "b must be a valid index";
        T valueAtA = values.get(a);
        T valueAtB = values.get(b);
        values.set(a, valueAtB);
        values.set(b, valueAtA);
    }


}

Step 3

/function/Function.java

package functions;

public interface Function<A, B> {

    public B apply(A source);

    default <C> Function<C, B> compose(Function<C, A> before) { //it should be ? super C, ? extends A
        if (before == null) {
            throw new IllegalArgumentException("null argument");
        }
        final Function<A, B> f = this;
        return new Function<C, B>() {
            @Override
            public B apply(C source) {
                return f.apply(before.apply(source));
            }
        };
    }

    default <C> Function<A, C> andThen(Function<B, C> g) { //it should be ? super B, ? extends C
        if (g == null) {
            throw new IllegalArgumentException("null argument");
        }
        final Function<A, B> f = this;
        return new Function<A, C>() {
            @Override
            public C apply(A source) {
                return g.apply(f.apply(source));
            }
        };
    }

}

Main.java

import java.util.LinkedList;
import java.util.List;

import functions.Function;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        List<Integer> negatives = new LinkedList<>();
        Function<Integer, Integer> negative = new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer source) {
                if (source == null) {
                    throw new IllegalArgumentException("source is null");
                }
                return -1 * source;
            }
        };
        for (Integer e : numbers) {
            negatives.add(negative.apply(e));
        }
        System.out.println(negatives.toString());
    }

}

Step 4

/function/Function.java

package functions;

public interface Function<A, B> {

    public B apply(A source);

    default <C> Function<C, B> compose(Function<C, A> before) { //it should be ? super C, ? extends A
        if (before == null) {
            throw new IllegalArgumentException("null argument");
        }
        final Function<A, B> f = this;
        return new Function<C, B>() {
            @Override
            public B apply(C source) {
                return f.apply(before.apply(source));
            }
        };
    }

    default <C> Function<A, C> andThen(Function<B, C> g) { //it should be ? super B, ? extends C
        if (g == null) {
            throw new IllegalArgumentException("null argument");
        }
        final Function<A, B> f = this;
        return new Function<A, C>() {
            @Override
            public C apply(A source) {
                return g.apply(f.apply(source));
            }
        };
    }

}

Main.java

import java.util.LinkedList;
import java.util.List;

import functions.Function;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        List<String> negativesAsString = new LinkedList<>();
        Function<Integer, Integer> negative = new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer source) {
                if (source == null) {
                    throw new IllegalArgumentException("source is null");
                }
                return -1 * source;
            }
        };
        Function<Integer, String> asString = new Function<Integer,String>() {
            @Override
            public String apply(Integer source) {
                return String.valueOf(source);
            }
        };
        Function<Integer, String> toNegativeString = asString.compose(negative);
        for (Integer e : numbers) {
            negativesAsString.add(toNegativeString.apply(e));
        }
        System.out.println(negativesAsString.toString());
    }

}

Step 5

/function/Function.java

package functions;

@FunctionalInterface
public interface Function<A, B> {

    public B apply(A source);

    default <C> Function<C, B> compose(Function<C, A> before) { //it should be ? super C, ? extends A
        if (before == null) {
            throw new IllegalArgumentException("null argument");
        }
        return (C t) -> apply(before.apply(t));
    }

    default <C> Function<A, C> andThen(Function<B, C> g) { //it should be ? super B, ? extends C
        if (g == null) {
            throw new IllegalArgumentException("null argument");
        }
        return (A t) -> g.apply(apply(t));
    }

}

Main.java

import java.util.LinkedList;
import java.util.List;

import functions.Function;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        List<String> negativesAsString = new LinkedList<>();
        Function<Integer, Integer> negative = (Integer v) -> {return -1 * v;};
        Function<Integer, String> intAsString = (Integer v) -> {return String.valueOf(v);};
        Function<Integer, String> toNegativeString = intAsString.compose(negative);
        for (Integer e : numbers) {
            negativesAsString.add(toNegativeString.apply(e));
        }
        System.out.println(negativesAsString.toString());
    }

}

Step 6

Main.java

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = new LinkedList<>();
        numbers.add(2);
        numbers.add(8);
        numbers.add(-2);
        numbers.add(0);
        numbers.add(18);
        numbers.add(1);
        numbers.add(3);
        numbers.add(7);
        System.out.println(numbers.toString());
        sort(numbers, (Integer a, Integer b) -> {return a - b;});
        System.out.println(numbers.toString());
    }

    public static <T> void sort(List<T> values, Comparator<? super T> comparator) {
        assert values != null : "values cannot be null";
        if (values.size() > 2) {
            for (int sorted = 0; sorted < values.size(); sorted++) {
                int indexOfMax = maximumIndexFrom(values, sorted, comparator);
                swap(values, sorted, indexOfMax);
            }
        }
    }

    private static <T> int maximumIndexFrom(List<T> values, int from, Comparator<? super T> comparator) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert from >= 0 && from < values.size() : "from must be a valid index";
        int maxIndex = from;
        T max = values.get(from);
        for (int i = from + 1; i < values.size(); i++) {
            T current = values.get(i);
            if (comparator.compare(current, max) > 0) {
                max = current;
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    private static <T> void swap(List<T> values, int a, int b) {
        assert values != null : "values cannot be null";
        assert values.size() > 0 : "values cannot be empty";
        assert a >= 0 && a < values.size() : "a must be a valid index";
        assert b >= 0 && b < values.size() : "b must be a valid index";
        T valueAtA = values.get(a);
        T valueAtB = values.get(b);
        values.set(a, valueAtB);
        values.set(b, valueAtA);
    }


}

Figure example

Coord2D.java

package figures;

public class Coord2D
{

    public static final Coord2D ZERO = new Coord2D(0.0f, 0.0f);

    private double x;
    private double y;

    public Coord2D(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public double getX()
    {
        return x;
    }

    public double getY()
    {
        return y;
    }

    @Override
    public String toString()
    {
        return "(" + x + ", " + y + ")";
    }
}

Circle.java

package figures;

public class Circle extends Figure
{

    private double radius;

    public Circle(double radius)
    {
        super();
        this.radius = radius;
    }

    public Circle(Coord2D center, double radius)
    {
        super(center);
        this.radius = radius;
    }

    public double area()
    {
        return Math.PI * Math.pow(radius, 2.0f);
    }

    public double perimeter()
    {
        return 2.0f * Math.PI * radius;
    }

    @Override
    public String toString()
    {
        return  "I'm a circle, my center is at " + center.toString() +
                "\n\tI have a radius of " + radius +
                "\n\tMy area is " + area() +
                "\n\tMy perimeter is " + perimeter();
    }

}

Figure.java

package figures;

public abstract class Figure
{
    protected Coord2D center;

    public Figure()
    {
        center = Coord2D.ZERO;
    }

    public Figure(Coord2D center)
    {
        this.center = center;
    }

    public Coord2D getCenter()
    {
        return center;
    }

    public abstract double area();

    public abstract double perimeter();

    public double volumeByExtension(double height)
    {
        return area() * height;
    }

    @Override
    public abstract String toString();

}

Main.java

import figures.*;

/**
 * This first iteration of figures, shows how each Figure instance (Circle, Rectangle, or Square) will call its own toString() method.
 * 
 * In this example you can also see that {@link Figure#volumeByExtension(double)} will be called on class Figure, but the area method
 * will be called on the corresponding implementing class according to the specific instance (Circle, Rectangle, or Square)
 * 
 * Finally, the maximum method is able to work because the type int supports the operator >
 * 
 * In the next iteration we will try to use maximum on specific Figures.
 */
public class Main {

    public static void main(String[] args) {

        Figure[] figures = new Figure[] {new Circle(2.0d), new Rectangle(2.0d, 3.0d), new Square(3.0d), new Circle(4.2d)};

        for (Figure figure : figures) {
            System.out.println(figure.toString());
            System.out.println("Volume by extension (by 3.2d): " + figure.volumeByExtension(3.2d));
        }

        int[] numbers = new int[] {2, 5, 42, 79, -10, 5, 9, 0, -13, 128};
        System.out.println("The maximum number is: " + maximum(numbers));

    }

    public static int maximum(int[] values) {
        assert values.length > 0 : "values cannot be empty";
        int max = values[0];
        for (int i = 1; i < values.length; i++) {
            if (values[i] > max) {
                max = values[i];
            }
        }
        return max;
    }

}

Python

Task 1 Complete implementation of all functions in strings.py.
Task 2 Complete implementation of all functions in array_dictionary.py.
Task 3 Create an “interface” Dictionary and implement it with a list of tuples.
Task 4 Implement a program in Python called strings_stats.py that will take \(n\) strings as command-line arguments, the first one will be considered as the main string, the rest will be considered as targets.

The program must print:

  1. The main string string, and it’s length (the format must be

    ()).

  2. The targets, and the length of each one (the format must be a comma-separated list of ()).

  3. For each target in targets, you must write a line with how many times it occurred in the main string (the format must be occurred in

    ).

  4. For each target in targets, you must write a line with the percentage of occurrences in the main string (the format must be occurred % in

    ). The occurrences_percentage must be calculated considering all target’s occurrences as 100%.

You must use the “interface” and one implementation of Dictionary to solve the previous task.

Task 5 Implement “figures” classes and a sort function based on the area of each figure (use a method compare_to).

Task 6 Can you have proper generic classes and methods in Python, justify?

dictionary.py

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import List, Tuple, Optional


class Dictionary(ABC):
    """A minimal dictionary interface mapping *str* keys to *int* values."""

    @abstractmethod
    def keys(self) -> List[str]:
        """Returns all keys present in the dictionary."""

    @abstractmethod
    def values(self) -> List[int]:
        """Returns all values present in the dictionary."""

    @abstractmethod
    def key_exists(self, key: str) -> bool:
        """Checks whether *key* is present in the dictionary."""

    @abstractmethod
    def get_value(self, key: str) -> Optional[int]:
        """Returns the value associated with *key* or ``None`` when the key is absent."""

    @abstractmethod
    def set_value(self, key: str, value: int) -> Optional[int]:
        """Sets *value* for *key* and returns the previous value if the key existed."""


class ListDictionary(Dictionary):
    """Dictionary implementation backed by a list of ``(key, value)`` tuples.

    The implementation purposefully avoids using ``append`` or ``break`` to comply
    with the constraints communicated during the session.
    """

    def __init__(self, initial: Optional[List[Tuple[str, int]]] = None):
        # Initialize the backing storage in a more explicit (and readable) way.
        if initial is None:
            self._data: List[Tuple[str, int]] = []
        else:
            # Make a shallow copy to avoid external mutation side-effects.
            self._data = initial[:]

    # ---------------------------------------------------------------------
    # Interface implementation
    # ---------------------------------------------------------------------

    def keys(self) -> List[str]:
        return [k for k, _ in self._data]

    def values(self) -> List[int]:
        return [v for _, v in self._data]

    def key_exists(self, key: str) -> bool:
        for k, _ in self._data:
            if k == key:
                return True
        return False

    def get_value(self, key: str) -> Optional[int]:
        for k, v in self._data:
            if k == key:
                return v
        return None

    def set_value(self, key: str, value: int) -> Optional[int]:
        old: Optional[int] = None
        found: bool = False
        for idx in range(len(self._data)):
            k, v = self._data[idx]
            if k == key:
                old = v
                self._data[idx] = (key, value)
                found = True  # no *break* used
        # If the key was not present, extend the list without using *append*
        if not found:
            self._data += [(key, value)]
        return old

    # ------------------------------------------------------------------
    # Convenience dunder methods (optional but handy)
    # ------------------------------------------------------------------

    def __len__(self) -> int:  # pragma: no cover (helper)
        return len(self._data)

    def __iter__(self):  # pragma: no cover (helper)
        return iter(self._data)

    def __repr__(self) -> str:  # pragma: no cover (helper)
        return f"ListDictionary({self._data})" 

figures.py

from __future__ import annotations

from abc import ABC, abstractmethod
from math import pi
from typing import List


class Figure(ABC):
    """Abstract geometric figure."""

    # ---------- required API --------------------------------------------
    @abstractmethod
    def area(self) -> float:  # pragma: no cover (abstract)
        """Returns the area of the figure."""

    # ------------------------------------------------------------------
    # Comparison helper – reused by *sort_figures*
    # ------------------------------------------------------------------

    def compare_to(self, other: "Figure") -> int:

        diff: float = self.area() - other.area()
        eps: float = 1e-9
        if abs(diff) < eps:
            return 0
        return -1 if diff < 0 else 1

    # Convenience so built-in ``sorted`` could be used if desired
    def __lt__(self, other: "Figure") -> bool:  # pragma: no cover (helper)
        return self.compare_to(other) < 0

    # Nicely printable representation
    def __repr__(self) -> str:  # pragma: no cover (helper)
        return f"{self.__class__.__name__}(area={self.area():.2f})"


class Circle(Figure):
    def __init__(self, radius: float):
        assert radius >= 0, "Radius must be non-negative"
        self.radius = radius

    def area(self) -> float:
        return pi * self.radius * self.radius


class Rectangle(Figure):
    def __init__(self, width: float, height: float):
        assert width >= 0 and height >= 0, "Dimensions must be non-negative"
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height


class Square(Rectangle):
    def __init__(self, side: float):
        super().__init__(side, side)


# ---------------------------------------------------------------------
# Sorting helper
# ---------------------------------------------------------------------

def sort_figures(figures: List[Figure]) -> List[Figure]:

    # Work on a shallow copy to keep the original list intact
    sorted_list: List[Figure] = figures[:]
    n: int = len(sorted_list)

    for i in range(n):
        # Find index of the minimum element in [i, n)
        min_idx: int = i
        for j in range(i + 1, n):
            if sorted_list[j].compare_to(sorted_list[min_idx]) < 0:
                min_idx = j
        # Swap positions if needed
        if min_idx != i:
            sorted_list[i], sorted_list[min_idx] = sorted_list[min_idx], sorted_list[i]

    return sorted_list 

list_dictionary.py

def keys(dictionary: list[(str, int)]) -> list[str]:
    return [key for key, _ in dictionary]

def values(dictionary: list[(str, int)]) -> list[int]:
    return [value for _, value in dictionary]

def key_exists(dictionary: list[(str, int)], key: str) -> bool:
    for key_value in dictionary:
        if key_value[0] == key:
            return True
    return False        

def get_value(dictionary: list[(str, int)], key: str) -> int | None:
    for key_value in dictionary:
        if key_value[0] == key:
            return key_value[1]
    return None

def set_value(dictionary: list[(str, int)], key: str, value: int) -> int | None:
    old_value: int | None = None
    found: bool = False
    for idx in range(len(dictionary)):
        k, v = dictionary[idx]
        if k == key:
            old_value = v
            dictionary[idx] = (key, value)
            found = True  # continue checking rest without breaking
    # If key was not found, add new entry without using append
    if not found:
        dictionary += [(key, value)]
    return old_value

string.py

def is_substring(haystack: str, needle: str) -> bool:
    return index_of(haystack, needle) != -1

def is_subsequence(haystack: str, needle: str) -> bool:
    # An empty needle is always a subsequence
    if len(needle) == 0:
        return True
    # Traverse both strings maintaining a pointer for the needle
    idx: int = 0  # pointer inside needle
    for c in haystack:
        if idx < len(needle) and c == needle[idx]:
            idx += 1
            if idx == len(needle):
                return True
    return False

def generate_all_substrings(string: str) -> list[str]:
    n: int = len(string)
    return [string[start:end] for start in range(n) for end in range(start + 1, n + 1)]


def generate_all_subsequences(string: str) -> list[str]:
    # Recursive generation without using append or bitwise operations
    if string == "":
        return [""]
    first_char: str = string[0]
    rest_subsequences: list[str] = generate_all_subsequences(string[1:])
    return rest_subsequences + [first_char + subseq for subseq in rest_subsequences]


def to_upper_case(string: str) -> str:
    upper: str = ""
    for c in string:
        if 'a' <= c <= 'z':
            upper += chr(ord(c) - 32)
        else:
            upper += c
    return upper


def to_lower_case(string: str) -> str:
    lower: str = ""
    for c in string:
        if 'A' <= c <= 'Z':
            lower += chr(ord(c) + 32)
        else:
            lower += c
    return lower


def reverse(string: str) -> str:
    rev: str = ""
    for c in string:
        rev = c + rev
    return rev

def count_substrings(string: str, substring: str) -> int: 
    if len(substring) == 0:
        return 1
    count: int = 0
    target_index: int = index_of(string, substring)
    while target_index != -1:
        count = count + 1
        string = string[target_index + len(substring)]
        target_index = index_of(string, substring)
    return count


def index_of(string: str, target: str) -> int:
    assert string is not None
    assert target is not None
    if len(target) == 0:
        return 0
    if len(string) == 0:
        return -1
    if len(target) > len(string):
        return -1
    found: bool = False
    idx: int = 0
    remaining_length: int = len(string)
    # My last index would be at len(string) - 1
    # At the start, the length of available string is len(string) - idx
    while not found and remaining_length >= len(target):
        if starts_with(string[idx:], target):
            found = True
        else:
            idx = idx + 1
            remaining_length = remaining_length - 1
    if not found:
        return -1
    return idx

def starts_with(string: str, target: str) -> bool:
    assert string is not None
    assert target is not None
    if len(target) == 0:
        return True
    if len(string) == 0:
        return False
    for i in range(len(target)):
        if string[i] != target[i]:
            return False
    return True

string_stats.py

from __future__ import annotations

import sys
from typing import List

import strings  # re-use `index_of` helper
from dictionary import ListDictionary


def count_occurrences(text: str, sub: str) -> int:
    if len(sub) == 0:
        # Convention borrowed from the earlier `count_substrings` definition
        return 1

    cnt: int = 0
    haystack: str = text
    pos: int = strings.index_of(haystack, sub)
    while pos != -1:
        cnt += 1
        haystack = haystack[pos + 1 :]
        pos = strings.index_of(haystack, sub)
    return cnt


def main(argv: List[str]) -> None:
    if len(argv) < 3:
        print("Usage: python strings_stats.py <main_string> <target1> <target2> ...")
        sys.exit(1)

    main_str: str = argv[1]
    targets: List[str] = argv[2:]

    # ------------------------------------------------------------------
    # Collect statistics using the Dictionary interface
    # ------------------------------------------------------------------

    stats = ListDictionary()
    for t in targets:
        stats.set_value(t, count_occurrences(main_str, t))

    total_occurrences: int = sum(stats.values())

    # ------------------------------------------------------------------
    # Output section 1: main string and its length
    # ------------------------------------------------------------------

    print(f"{main_str} ({len(main_str)})")

    # ------------------------------------------------------------------
    # Output section 2: targets and their lengths (comma-separated list)
    # ------------------------------------------------------------------

    targets_with_len = ", ".join(f"{t} ({len(t)})" for t in targets)
    print(targets_with_len)

    # ------------------------------------------------------------------
    # Output section 3: raw occurrence counts per target
    # ------------------------------------------------------------------

    for t in targets:
        occurrences = stats.get_value(t) or 0
        print(f"{t} occurred {occurrences} in {main_str}")

    # ------------------------------------------------------------------
    # Output section 4: percentage of occurrences per target
    # ------------------------------------------------------------------

    for t in targets:
        occurrences = stats.get_value(t) or 0
        percentage = 0.0 if total_occurrences == 0 else (occurrences * 100) / total_occurrences
        # Format percentage with two decimal places
        print(f"{t} occurred {percentage:.2f}% in {main_str}")


if __name__ == "__main__":
    main(sys.argv) 

Assignment with = and copy constructor and destructor

Usually copy value, copy reference for objects (shallow copy by default).

Shllow copy: copy each field

  • Double free issue
  • It is good to override assignment operator.

1

#include <iostream>

class IntWrapper {
    private:
        int value;

    public:

        IntWrapper(int value): value(value) {
            std::cout << "Constructor called" << std::endl;
        };

        IntWrapper(const IntWrapper& other): value(other.value) {
            std::cout << "Copy constructor called" << std::endl;
        };

        ~IntWrapper() {
            std::cout << "not much to do here! (my value was " << value << ")" << std::endl;
        }

        void change_value(const int new_value) {
            value = new_value;
        }

        friend std::ostream & operator << (std::ostream &out, const IntWrapper &c);
};

std::ostream & operator << (std::ostream &out, const IntWrapper &c) {
    out << "Wrapping(" << c.value << ")";
    return out;
}

int main() {
    IntWrapper w_1(1);
    IntWrapper w_2 = w_1;
    IntWrapper w_3 = w_1;
    w_3 = w_2; // this is an assignment
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we change w_1" << std::endl;
    w_1.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we restore w_1 and change w_3" << std::endl;
    w_1.change_value(1);
    w_3.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    return 1;
}

2

#include <iostream>

class IntWrapper {
    private:
        int * value;

    public:

        IntWrapper(int value) {
            this->value = new(int);
            *this->value = value;
            std::cout << "Constructor called" << std::endl;
        };

        IntWrapper(const IntWrapper& other): IntWrapper(*other.value) {};

        ~IntWrapper() {
            std::cout << "Need to free value now! (my value was " << (*value) << ")" << std::endl;
            delete(value);
        }

        void change_value(const int new_value) {
            *value = new_value;
        }

        friend std::ostream & operator << (std::ostream &out, const IntWrapper &c);
};

std::ostream & operator << (std::ostream &out, const IntWrapper &c) {
    out << "Wrapping(" << *(c.value) << ")";
    return out;
}

int main() {
    IntWrapper w_1(1);
    IntWrapper w_2 = w_1;
    IntWrapper w_3 = w_1;
    w_3 = w_2; // this is an assignment
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we change w_1" << std::endl;
    w_1.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we restore w_1 and change w_3" << std::endl;
    w_1.change_value(1);
    w_3.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    return 1;
}

3

#include <iostream>

class IntWrapper {
    private:
        int * value;

    public:

        IntWrapper(int value) {
            this->value = new(int);
            *this->value = value;
            std::cout << "Constructor called" << std::endl;
        };

        IntWrapper(const IntWrapper& other): IntWrapper(*other.value) {};

        ~IntWrapper() {
            std::cout << "Need to free value now! (my value was " << (*value) << ")" << std::endl;
            delete(value);
        }

        void change_value(const int new_value) {
            *value = new_value;
        }

        friend std::ostream & operator << (std::ostream &out, const IntWrapper &c);

        IntWrapper& operator=(const IntWrapper& other) {
            if (&other != this) {
                *(this->value) = *(other.value);
            }
            return *this;
        }
};

std::ostream & operator << (std::ostream &out, const IntWrapper &c) {
    out << "Wrapping(" << *(c.value) << ")";
    return out;
}

int main() {
    IntWrapper w_1(1);
    IntWrapper w_2 = w_1;
    IntWrapper w_3 = w_1;
    w_3 = w_2; // this is an assignment
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we change w_1" << std::endl;
    w_1.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    std::cout << "Now we restore w_1 and change w_3" << std::endl;
    w_1.change_value(1);
    w_3.change_value(42);
    std::cout << w_1 << std::endl;
    std::cout << w_2 << std::endl;
    std::cout << w_3 << std::endl;
    return 1;
}

SinglyLinkedListWithCache.java

package collections;

import functions.Action;
import functions.BinaryFunction;
import functions.Function;
import functions.Predicate;

/**
 * An singly-linked node implementation of a list, but with a node cache to reuse nodes.
 * @see {@link collections.List}
 * @version 0.1
 */
public class SinglyLinkedListWithCache<T> extends AbstractList<T> {

    /**
     * Head pointer for this list (may be {@code null} for an empty list}
     */
    private Node<T> head;

    /**
     * Cached size of this list. Always non-negative.
     */
    private int size;

    /* =============================================================
     *  Node cache
     * ============================================================= */

    /**
     * Head of the singly-linked list that acts as the node cache.
     */
    private Node<T> cacheHead;

    /**
     * Number of nodes currently stored in the cache.
     */
    private int cacheSize;

    /**
     * Maximum number of nodes that the cache will store. Once this capacity is
     * reached, additional removed nodes will be allowed to be reclaimed by the
     * garbage collector.
     */
    private final int cacheCapacity;

    /**
     * Builds an empty list whose cache can hold up to {@code cacheCapacity}
     * nodes.
     *
     * @param cacheCapacity maximum number of nodes to keep in the cache. A
     *                      negative value is interpreted as <em>no cache</em>.
     */
    public SinglyLinkedListWithCache(int cacheCapacity) {
        if (cacheCapacity < 0) {
            throw new IllegalArgumentException("cacheCapacity must be >= 0");
        }
        this.cacheCapacity = cacheCapacity;
        this.head = null;
        this.size = 0;
        this.cacheHead = null;
        this.cacheSize = 0;
    }

    /**
     * Builds an empty list with a default cache capacity of {@code 100}
     * nodes.
     */
    public SinglyLinkedListWithCache() {
        this(100);
    }

    // ---------------------------------------------------------------------
    // Helper methods
    // ---------------------------------------------------------------------

    /**
     * Pops a node from the cache, initialising it with the supplied value. If
     * the cache is empty a brand-new node will be allocated.
     */
    private Node<T> allocateNode(T value) {
        if (cacheHead != null) {
            Node<T> node = cacheHead;
            cacheHead = cacheHead.getNext();
            cacheSize--;
            node.setValue(value);
            node.setNext(null);
            return node;
        }
        return new Node<>(value);
    }

    /**
     * Adds the supplied node to the cache – provided the cache did not yet
     * reach the pre-defined capacity. The node's value is cleared to help the
     * garbage collector.
     */
    private void cacheNode(Node<T> node) {
        if (cacheSize >= cacheCapacity || node == null) {
            return; // let the GC reclaim the node
        }
        node.setValue(null);
        node.setNext(cacheHead);
        cacheHead = node;
        cacheSize++;
    }

    /**
     * Returns the node located at a given index. Helper method that assumes
     * the index is valid (0 ≤ index < size).
     */
    private Node<T> nodeAt(int index) {
        Node<T> current = head;
        for (int i = 0; i < index; i++) {
            current = current.getNext();
        }
        return current;
    }

    @Override
    public T get(int i) {
        if (i < 0 || i >= size) {
            throw new IllegalArgumentException("index out of range");
        }
        return nodeAt(i).getValue();
    }

    @Override
    public void add(T elem) {
        add(elem, size); // append is just insert at the end
    }

    @Override
    public void add(T elem, int i) {
        if (i < 0 || i > size) {
            throw new IllegalArgumentException("index out of range");
        }
        Node<T> newNode = allocateNode(elem);
        if (i == 0) { // insert at head
            newNode.setNext(head);
            head = newNode;
        } else {
            Node<T> prev = nodeAt(i - 1);
            newNode.setNext(prev.getNext());
            prev.setNext(newNode);
        }
        size++;
    }

    @Override
    public void remove(T elem) {
        Node<T> prev = null;
        Node<T> current = head;
        while (current != null) {
            if (java.util.Objects.equals(current.getValue(), elem)) {
                if (prev == null) {
                    head = current.getNext();
                } else {
                    prev.setNext(current.getNext());
                }
                size--;
                cacheNode(current);
                return;
            }
            prev = current;
            current = current.getNext();
        }
    }

    @Override
    public void remove(int i) {
        if (i < 0 || i >= size) {
            throw new IllegalArgumentException("index out of range");
        }
        Node<T> removed;
        if (i == 0) {
            removed = head;
            head = head.getNext();
        } else {
            Node<T> prev = nodeAt(i - 1);
            removed = prev.getNext();
            prev.setNext(removed == null ? null : removed.getNext());
        }
        size--;
        cacheNode(removed);
    }

    @Override
    public <E> List<E> map(Function<? super T, ? extends E> function) {
        if (function == null) {
            throw new IllegalArgumentException("null argument");
        }
        SinglyLinkedListWithCache<E> result = new SinglyLinkedListWithCache<>(cacheCapacity);
        Node<T> current = head;
        while (current != null) {
            result.add(function.apply(current.getValue()));
            current = current.getNext();
        }
        return result;
    }

    @Override
    public boolean all(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        Node<T> current = head;
        while (current != null) {
            if (!predicate.test(current.getValue())) {
                return false;
            }
            current = current.getNext();
        }
        return true;
    }

    @Override
    public boolean any(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        Node<T> current = head;
        while (current != null) {
            if (predicate.test(current.getValue())) {
                return true;
            }
            current = current.getNext();
        }
        return false;
    }

    @Override
    public List<T> filter(Predicate<? super T> predicate) {
        if (predicate == null) {
            throw new IllegalArgumentException("null argument");
        }
        SinglyLinkedListWithCache<T> result = new SinglyLinkedListWithCache<>(cacheCapacity);
        Node<T> current = head;
        while (current != null) {
            T value = current.getValue();
            if (predicate.test(value)) {
                result.add(value);
            }
            current = current.getNext();
        }
        return result;
    }

    @Override
    public void forEach(Action<? super T> action) {
        if (action == null) {
            throw new IllegalArgumentException("null argument");
        }
        Node<T> current = head;
        while (current != null) {
            action.consume(current.getValue());
            current = current.getNext();
        }
    }

    @Override
    public T reduce(T identity, BinaryFunction<T, T, T> accumulator) {
        if (accumulator == null) {
            throw new IllegalArgumentException("null argument");
        }
        T result = identity;
        Node<T> current = head;
        while (current != null) {
            result = accumulator.apply(result, current.getValue());
            current = current.getNext();
        }
        return result;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    // ---------------------------------------------------------------------
    // java.lang.Object overrides
    // ---------------------------------------------------------------------

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof List<?>)) {
            return false;
        }
        List<?> o = (List<?>) other;
        if (o.size() != this.size) {
            return false;
        }
        for (int i = 0; i < size; i++) {
            if (!java.util.Objects.equals(this.get(i), o.get(i))) {
                return false;
            }
        }
        return true;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        Node<T> current = head;
        while (current != null) {
            sb.append(String.valueOf(current.getValue()));
            current = current.getNext();
            if (current != null) {
                sb.append(',');
            }
        }
        sb.append(']');
        return sb.toString();
    }

}

Lambda Expression

import java.util.List;

public class Main {

    public static Integer abs(Integer a) {
        return a < 0? (-1 * a) : a;
    }

    public static Function<Integer, Integer> addition(Integer a) {
        return (new AddToValue(a))::apply;
    }

    public static Function<Integer, Function<Integer, Integer>> maximumOfThree(Integer a) {
        return (new MaximumOfTwoAgainstFixedValued(a))::apply;
    }

    public static void main(String[] args) {
        //Absolute
        Function<Integer, Integer> abs_wc = new Abs(); //Using a Class
        Integer absRes_wc = abs_wc.apply(-42);

        Function<Integer, Integer> abs_wac = new Function<Integer,Integer>() {
            @Override
            public Integer apply(Integer v) {
                return v < 0? (-1 * v) : v;
            }
        }; //Using Anonymous Class
        Integer absRes_wac = abs_wac.apply(-42);

        Function<Integer, Integer> abs_wle = (Integer v) -> {return v < 0? (-1 * v) : v;};
        //Using Lambda Expression
        Integer absRec_wle = abs_wle.apply(-42);

        Function<Integer, Integer> abs_wmr = Main::abs;

        Function<Integer, Integer> identity = Integer::new;

        //Addition (a + b = res)
        Function<Integer, Function<Integer, Integer>> addition_wc = new Addition();
        Integer res_wc = addition_wc.apply(2).apply(3);

        Function<Integer, Function<Integer, Integer>> addition_wac = new Function<Integer,Function<Integer,Integer>>() {

            @Override
            public Function<Integer, Integer> apply(Integer a) {
                return new Function<Integer,Integer>() {

                    @Override
                    public Integer apply(Integer b) {
                        return a + b;
                    }

                };
            }

        }; //With Anonymous Class
        Integer res_wac = addition_wac.apply(2).apply(3);

        Function<Integer, Function<Integer, Integer>> addition_wle_1 =
        (Integer a) -> (Integer b) -> {return a + b;};

        Function<Integer, Function<Integer, Integer>> addition_wle_2 =
        (Integer a) -> {return (Integer b) -> {return a + b;};};
        Integer res_wle = addition_wle_1.apply(2).apply(3);

        Function<Integer, Function<Integer, Integer>> addition_wmr = Main::addition;
        Integer res_wmr = addition_wmr.apply(2).apply(3);

        //Maximum of three
        Function<Integer, Function<Integer, Function<Integer, Integer>>> maxThree_wc = new MaximumOfThree();
        System.out.println("Maximum of 3, 1, 2 is " + maxThree_wc.apply(3).apply(1).apply(2));

        Function<Integer, Function<Integer, Function<Integer, Integer>>> maxThree_wac = new Function<Integer,Function<Integer,Function<Integer,Integer>>>() {

            @Override
            public Function<Integer, Function<Integer, Integer>> apply(Integer a) {
                return new Function<Integer,Function<Integer,Integer>>() {

                    @Override
                    public Function<Integer,Integer> apply(Integer b) {
                        return new Function<Integer,Integer>() {

                            @Override
                            public Integer apply(Integer c) {
                                if (a >= b && a >= c) {
                                    return a;
                                } else if (b >= a && b >= c) {
                                    return b;
                                } else {
                                    return c;
                                }
                            }

                        };
                    }

                };
            }


        };
        System.out.println("Maximum of 3, 1, 2 is " + maxThree_wac.apply(3).apply(1).apply(2));
        Function<Integer, Function<Integer, Function<Integer, Integer>>> maxThree_wle =
        (Integer a) -> (Integer b) -> (Integer c) -> {
            if (a >= b && a >= c) {
                return a;
            } else if (b >= a && b >= c) {
                return b;
            } else {
                return c;
            }
        };
        Function<Integer, Function<Integer, Function<Integer, Integer>>> maxThree_wmr = Main::maximumOfThree;

        Function<List<Integer>, Function<Integer, Integer>> listGet_wc = new ListGet();
        Function<List<Integer>, Function<Integer, Integer>> listGet_wax = new Function<List<Integer>,Function<Integer,Integer>>() {

            @Override
            public Function<Integer, Integer> apply(List<Integer> l) {
                return new Function<Integer,Integer>() {

                    @Override
                    public Integer apply(Integer index) {
                        return l.get(index);
                    }

                };
            }

        };
        Function<List<Integer>, Function<Integer, Integer>> listGet_wle =
        (List<Integer> l) -> (Integer index) -> {return l.get(index);};
        Function<List<Integer>, Function<Integer, Integer>> listGet_wmr =
        (List<Integer> l) -> {return l::get;};

        Function<Integer, Integer> factorial =
        (Integer n) -> {
            Integer res = 1;
            for (int i = n; i > 0; i--) {
                res *= i;
            }
            return res;
        };
        Integer res_factorial = factorial.apply(3);
        System.out.println(res_factorial);
    }
}