{"id":"list-and-map-internals-generic-list","slug":"generic-list","title":"Any type: a generic list","text":"Playlist holds Strings, and nothing else. A list of whole numbers, or of objects of your own class, would be the same class again with another type in it. Module 44's type parameters are made for this: write the class once, as `SimpleList<T>`, with T wherever Playlist said String.\n\n### The array is the snag\n\nThe instance variable becomes `private T[] values;`, an array of T. But creating it, the obvious way, doesn't compile:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        SimpleList<String> songs = new SimpleList<>();\n        System.out.println(\"A new list\");\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<T> {\n    private T[] values;\n    private int size;\n\n    public SimpleList() {\n        this.values = new T[4];\n        this.size = 0;\n    }\n}\n```\n\n```javac\nSimpleList.java:6: error: generic array creation\n        this.values = new T[4];\n                      ^\n1 error\n```\n\nAn array knows the type of its elements while the program runs: an array created as `new String[4]` stays an array of Strings. So to create an array, Java must know the element type at that moment. Type parameters don't last that long. javac uses T to check the code, and then the type argument is gone: the running program doesn't know whether a SimpleList was made for Strings or for Integers. So `new T[4]` can't be created.\n\nThe way around it is an array of Object, which can hold any object, since every class inherits from Object (module 34). Cast it to `T[]`, and the rest of the class can treat it as an array of T:\n\n```java\nthis.values = (T[]) new Object[4];\n```\n\nJava's own ArrayList keeps an Object array inside as well.\n\n### javac warns about the cast\n\nWith that line, the class compiles and runs, but javac prints a note:\n\n```text\nNote: SimpleList.java uses unchecked or unsafe operations.\nNote: Recompile with -Xlint:unchecked for details.\n```\n\nIt's a warning, not an error. \"Unchecked\" means that javac can't check this cast: it doesn't know what T will be, so it can't promise that the array really fits. Here the cast is safe, for a reason javac can't see: the array never leaves the class, and the only values that go into it come through `add(T value)`, which takes nothing but T.\n\nTo tell javac that you've checked it, put the annotation `@SuppressWarnings(\"unchecked\")` above each method or constructor that has such a cast. Like `@Override`, it's a note to javac. It turns the warning off for that one method, and changes nothing else. The code in this course uses it, so that javac stays quiet. If you leave it out, you'll see the note, and the program still runs.\n\n### SimpleList\n\nHere's the whole class: Playlist with T in place of String, and a cast in the constructor and in `grow`, the two places that create an array:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        SimpleList<String> songs = new SimpleList<>();\n        songs.add(\"Night Train\");\n        songs.add(\"Paper Boats\");\n        System.out.println(songs + \" \" + songs.contains(\"Paper Boats\"));\n\n        SimpleList<Integer> minutes = new SimpleList<>();\n        int[] lengths = {4, 3, 5, 6, 2};\n        for (int length : lengths) {\n            minutes.add(length);\n        }\n        int total = 0;\n        for (int i = 0; i < minutes.size(); i++) {\n            total += minutes.get(i);\n        }\n        System.out.println(minutes + \", \" + total + \" minutes in all\");\n\n        minutes.remove(1);\n        minutes.remove(Integer.valueOf(6));\n        System.out.println(minutes);\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<T> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public SimpleList() {\n        this.values = (T[]) new Object[4];\n        this.size = 0;\n    }\n\n    public void add(T value) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void grow() {\n        T[] bigger = (T[]) new Object[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public int size() {\n        return this.size;\n    }\n\n    public T get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        return this.values[index];\n    }\n\n    public int indexOf(T value) {\n        for (int i = 0; i < this.size; i++) {\n            if (this.values[i].equals(value)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    public boolean contains(T value) {\n        return indexOf(value) >= 0;\n    }\n\n    public T remove(int index) {\n        T removed = get(index);\n        for (int i = index; i < this.size - 1; i++) {\n            this.values[i] = this.values[i + 1];\n        }\n        this.values[this.size - 1] = null;\n        this.size--;\n        return removed;\n    }\n\n    public boolean remove(T value) {\n        int index = indexOf(value);\n        if (index < 0) {\n            return false;\n        }\n        remove(index);\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        String text = \"[\";\n        for (int i = 0; i < this.size; i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.values[i];\n        }\n        return text + \"]\";\n    }\n}\n```\n\n```output\n[Night Train, Paper Boats] true\n[4, 3, 5, 6, 2], 20 minutes in all\n[4, 5, 2]\n```\n\n- One class, two lists: `SimpleList<String>` and `SimpleList<Integer>`. For each of them, javac reads every T as the type argument, so `minutes.get(i)` gives an Integer, and `total += minutes.get(i);` needs no cast.\n- `indexOf` compares with `equals`, which every object has, so the search works for any type. Integer's `equals` compares the numbers. For a class of your own, write an `equals` (module 32): without one, `equals` is true only for the very same object, and `contains` can't find an equal copy.\n- `toString` adds each value to the text, which calls the value's own `toString`.\n- The type argument must be a reference type, not a primitive type, as module 44 showed: `SimpleList<Integer>`, not `SimpleList<int>`. Java turns `4` into an Integer when it goes in, and back into an int when it comes out.\n- In a `SimpleList<Integer>`, `remove(1)` removes the value at index 1, because 1 is an int and `remove(int index)` takes an int as it is. `remove(Integer.valueOf(6))` gives an Integer, which matches `remove(T value)`. It's the same rule as for ArrayList in module 14, and now you can see where it comes from: the two methods in your own class.\n","task":"A market stall keeps its products and their prices in two lists of one generic class. Fill in the three blanks of `SimpleList.java`: the type parameter in the class header, the class of the array that's created, and the return type of `get`. The program prints:\n\n```\npears: 6.5 in all\n```\n","kind":"fill","seed":"public class Main {\n    public static void main(String[] args) {\n        SimpleList<String> products = new SimpleList<>();\n        products.add(\"apples\");\n        products.add(\"pears\");\n        SimpleList<Double> prices = new SimpleList<>();\n        prices.add(2.5);\n        prices.add(4.0);\n        System.out.println(products.get(1) + \": \" + (prices.get(0) + prices.get(1)) + \" in all\");\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<[[T]]> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public SimpleList() {\n        this.values = (T[]) new [[Object]][4];\n        this.size = 0;\n    }\n\n    public void add(T value) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void grow() {\n        T[] bigger = (T[]) new Object[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public [[T]] get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        return this.values[index];\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        SimpleList<String> products = new SimpleList<>();\n        products.add(\"apples\");\n        products.add(\"pears\");\n        SimpleList<Double> prices = new SimpleList<>();\n        prices.add(2.5);\n        prices.add(4.0);\n        System.out.println(products.get(1) + \": \" + (prices.get(0) + prices.get(1)) + \" in all\");\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<T> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public SimpleList() {\n        this.values = (T[]) new Object[4];\n        this.size = 0;\n    }\n\n    public void add(T value) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void grow() {\n        T[] bigger = (T[]) new Object[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public T get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        return this.values[index];\n    }\n}\n","hints":["The rest of the class already says T: `private T[] values;` and `add(T value)`.","The class header is `public class SimpleList<T>`, and `get` returns a T. `new T[4]` isn't allowed, so the array created is an array of `Object`, cast to `T[]`, as `grow` does."],"tests":[{"name":"Output","stdin":"","expect":"pears: 6.5 in all","hidden":false}],"require":[],"forbid":[],"more":[{"task":"`SimpleList.java` is the class from this step. A list of Integers has two `remove` methods that both seem to fit. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        SimpleList<Integer> scores = new SimpleList<>();\n        int[] values = {3, 1, 4, 1, 5};\n        for (int value : values) {\n            scores.add(value);\n        }\n        scores.remove(1);\n        System.out.println(scores);\n        scores.remove(Integer.valueOf(1));\n        System.out.println(scores);\n        System.out.println(scores.remove(Integer.valueOf(9)) + \" \" + scores.indexOf(5));\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<T> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public SimpleList() {\n        this.values = (T[]) new Object[4];\n        this.size = 0;\n    }\n\n    public void add(T value) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void grow() {\n        T[] bigger = (T[]) new Object[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public T get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        return this.values[index];\n    }\n\n    public int indexOf(T value) {\n        for (int i = 0; i < this.size; i++) {\n            if (this.values[i].equals(value)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    public T remove(int index) {\n        T removed = get(index);\n        for (int i = index; i < this.size - 1; i++) {\n            this.values[i] = this.values[i + 1];\n        }\n        this.values[this.size - 1] = null;\n        this.size--;\n        return removed;\n    }\n\n    public boolean remove(T value) {\n        int index = indexOf(value);\n        if (index < 0) {\n            return false;\n        }\n        remove(index);\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        String text = \"[\";\n        for (int i = 0; i < this.size; i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.values[i];\n        }\n        return text + \"]\";\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        SimpleList<Integer> scores = new SimpleList<>();\n        int[] values = {3, 1, 4, 1, 5};\n        for (int value : values) {\n            scores.add(value);\n        }\n        scores.remove(1);\n        System.out.println(scores);\n        scores.remove(Integer.valueOf(1));\n        System.out.println(scores);\n        System.out.println(scores.remove(Integer.valueOf(9)) + \" \" + scores.indexOf(5));\n    }\n}\n// ==== SimpleList.java ====\npublic class SimpleList<T> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public SimpleList() {\n        this.values = (T[]) new Object[4];\n        this.size = 0;\n    }\n\n    public void add(T value) {\n        if (this.size == this.values.length) {\n            grow();\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void grow() {\n        T[] bigger = (T[]) new Object[this.values.length * 2];\n        for (int i = 0; i < this.size; i++) {\n            bigger[i] = this.values[i];\n        }\n        this.values = bigger;\n    }\n\n    public T get(int index) {\n        if (index < 0 || index >= this.size) {\n            throw new IndexOutOfBoundsException(\"Index \" + index + \" out of bounds for size \" + this.size);\n        }\n        return this.values[index];\n    }\n\n    public int indexOf(T value) {\n        for (int i = 0; i < this.size; i++) {\n            if (this.values[i].equals(value)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    public T remove(int index) {\n        T removed = get(index);\n        for (int i = index; i < this.size - 1; i++) {\n            this.values[i] = this.values[i + 1];\n        }\n        this.values[this.size - 1] = null;\n        this.size--;\n        return removed;\n    }\n\n    public boolean remove(T value) {\n        int index = indexOf(value);\n        if (index < 0) {\n            return false;\n        }\n        remove(index);\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        String text = \"[\";\n        for (int i = 0; i < this.size; i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.values[i];\n        }\n        return text + \"]\";\n    }\n}\n","hints":["`scores.remove(1)` gives an int, and `remove(int index)` takes an int as it is, so it removes the value at index 1, whatever that value is.","`Integer.valueOf(1)` is an Integer, which matches `remove(T value)`: it removes the first value that equals 1. The value 9 isn't there, so that `remove` returns false. Count the indexes again after the removals."],"tests":[{"name":"Output","stdin":"","expect":"[3, 4, 1, 5]\n[3, 4, 5]\nfalse 2","hidden":false}],"require":[],"forbid":[],"lines":["[3, 4, 1, 5]","[3, 4, 5]","false 2"]},{"task":"A card game keeps a discard pile: each card goes on top, and a player can take the top card, the one put there last. In `Pile.java`, write the generic class `Pile<T>`, on top of an array of its own, with:\n\n- a constructor with no parameters, which creates the array with room for 4 values, as `(T[]) new Object[4]`\n- `put(T value)`, which puts a value on top; when the array is full, it first moves the values into an array twice as long\n- `take()`, which removes the top value from the pile and returns it. On an empty pile, it throws an IllegalStateException with the message `The pile is empty`.\n- `size()`, which returns the number of values in the pile\n\nNo ArrayList: the top of the pile is simply the last used place of the array. The check runs:\n\n```java\nPile<String> discards = new Pile<>();\ndiscards.put(\"7 of hearts\");\ndiscards.put(\"queen of spades\");\ndiscards.put(\"2 of clubs\");\nSystem.out.println(discards.take());\nSystem.out.println(discards.take() + \", \" + discards.size() + \" left\");\n```\n\nand expects:\n\n```\n2 of clubs\nqueen of spades, 1 left\n```\n","kind":"code","seed":"// ==== Pile.java ====\npublic class Pile<T> {\n\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Pile<String> discards = new Pile<>();\n        discards.put(\"7 of hearts\");\n        discards.put(\"queen of spades\");\n        discards.put(\"2 of clubs\");\n        System.out.println(discards.take());\n        System.out.println(discards.take() + \", \" + discards.size() + \" left\");\n    }\n}\n","solution":"// ==== Pile.java ====\npublic class Pile<T> {\n    private T[] values;\n    private int size;\n\n    @SuppressWarnings(\"unchecked\")\n    public Pile() {\n        this.values = (T[]) new Object[4];\n        this.size = 0;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void put(T value) {\n        if (this.size == this.values.length) {\n            T[] bigger = (T[]) new Object[this.values.length * 2];\n            for (int i = 0; i < this.size; i++) {\n                bigger[i] = this.values[i];\n            }\n            this.values = bigger;\n        }\n        this.values[this.size] = value;\n        this.size++;\n    }\n\n    public T take() {\n        if (this.size == 0) {\n            throw new IllegalStateException(\"The pile is empty\");\n        }\n        T top = this.values[this.size - 1];\n        this.values[this.size - 1] = null;\n        this.size--;\n        return top;\n    }\n\n    public int size() {\n        return this.size;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Pile<String> discards = new Pile<>();\n        discards.put(\"7 of hearts\");\n        discards.put(\"queen of spades\");\n        discards.put(\"2 of clubs\");\n        System.out.println(discards.take());\n        System.out.println(discards.take() + \", \" + discards.size() + \" left\");\n    }\n}\n","hints":["Two instance variables, as in SimpleList: `private T[] values;` and `private int size;`. The constructor sets `this.values = (T[]) new Object[4];`, with `@SuppressWarnings(\"unchecked\")` above it if you want javac to stay quiet.","`put` works like SimpleList's `add`. `take` checks `this.size == 0` first and throws; otherwise it keeps `this.values[this.size - 1]` in a variable, sets that place to `null`, lowers the size, and returns the value. Nothing has to move: the top is at the end."],"tests":[{"name":"Test 1","stdin":"","call":"Pile<String> discards = new Pile<>();\ndiscards.put(\"7 of hearts\");\ndiscards.put(\"queen of spades\");\ndiscards.put(\"2 of clubs\");\nSystem.out.println(discards.take());\nSystem.out.println(discards.take() + \", \" + discards.size() + \" left\");\n","expect":"2 of clubs\nqueen of spades, 1 left","hidden":false},{"name":"Test 2","stdin":"","call":"Pile<Integer> numbers = new Pile<>();\nfor (int i = 1; i <= 10; i++) {\n    numbers.put(i * i);\n}\nSystem.out.println(numbers.size());\nint sum = 0;\nwhile (numbers.size() > 0) {\n    int top = numbers.take();\n    sum += top;\n    if (numbers.size() % 3 == 0) {\n        System.out.print(top + \" \");\n    }\n}\nSystem.out.println(sum);\ntry {\n    numbers.take();\n    System.out.println(\"took one\");\n} catch (IllegalStateException e) {\n    System.out.println(e.getMessage());\n}\n","expect":"10\n100 49 16 1 385\nThe pile is empty","hidden":true},{"name":"Test 3","stdin":"","call":"Pile<String> pile = new Pile<>();\ntry {\n    pile.take();\n    System.out.println(\"took one\");\n} catch (IllegalStateException e) {\n    System.out.println(e.getMessage());\n}\npile.put(\"ace\");\nSystem.out.println(pile.take() + \" \" + pile.size());\npile.put(\"king\");\npile.put(\"jack\");\nSystem.out.println(pile.take() + \" \" + pile.size());\n","expect":"The pile is empty\nace 0\njack 1","hidden":true}],"require":[{"pattern":"\\(\\s*T\\s*\\[\\s*\\]\\s*\\)\\s*new\\s+Object\\s*\\[","message":"Create the array as `(T[]) new Object[4]`: `new T[4]` doesn't compile."},{"pattern":"\\bthrow\\s+new\\s+IllegalStateException\\s*\\(","message":"Refuse `take` on an empty pile with `throw new IllegalStateException(\"The pile is empty\")`."}],"forbid":[{"pattern":"\\b(ArrayList|LinkedList|List|ArrayDeque|Stack)\\b","message":"Keep the values in an array of your own, without Java's collections."}]}]}