{"id":"list-and-map-internals-fixed-size","slug":"fixed-size","title":"An array's size is fixed","text":"Since module 13 you've added to lists without a thought about room: `add` always works, however many elements the list holds already. Since module 31, a HashMap has found a value by its key in a moment. This module opens both of them up. You'll build your own list and your own hash map, on top of plain arrays, and see why `add` never runs out of room and why a lookup in a map stays fast. Java's own ArrayList and HashMap are more refined, and you'll go on using them, but they work on the same ideas.\n\n### Arrays, a quick recap\n\nModule 15's arrays hold a fixed number of values of one type. `new String[3]` makes room for three Strings at the indexes 0 to 2, and each place starts out as `null`. `length` tells how many places there are:\n\n```java\nString[] playlist = new String[3];\nplaylist[0] = \"Morning Rain\";\nplaylist[1] = \"Night Train\";\nSystem.out.println(playlist.length);\nSystem.out.println(playlist[1]);\nSystem.out.println(playlist[2]);\n```\n\n```output\n3\nNight Train\nnull\n```\n\nAn array keeps the size it was created with for as long as it exists. There's no way to add a fourth place to this one:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        String[] playlist = new String[3];\n        playlist[0] = \"Morning Rain\";\n        playlist[1] = \"Night Train\";\n        playlist[2] = \"Paper Boats\";\n        playlist[3] = \"Slow River\";\n        System.out.println(\"Four songs added\");\n    }\n}\n```\n\n```crash\nException in thread \"main\" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3\n\tat Main.main(Main.java:7)\n```\n\nA list can't work like that: it has to take a new element whenever you call `add`. Two tricks make that possible.\n\n### Room to spare, and a count\n\nThe first trick: make the array bigger than you need, and keep count of how many places are in use. Places 0 to count - 1 hold values, and the rest wait for more:\n\n```java\nString[] songs = new String[5];\nint count = 0;\n\nsongs[count] = \"Morning Rain\";\ncount++;\nsongs[count] = \"Night Train\";\ncount++;\n\nSystem.out.println(count + \" songs, room for \" + songs.length);\nfor (int i = 0; i < count; i++) {\n    System.out.println(i + \": \" + songs[i]);\n}\n```\n\n```output\n2 songs, room for 5\n0: Morning Rain\n1: Night Train\n```\n\n- `songs[count] = ...;` followed by `count++;` puts a value in the first free place and moves the count on. The count is two things at once: the number of songs, and the index of the next free place.\n- The loop stops at `count`, not at `songs.length`. Places 2 to 4 hold `null`, not songs.\n\nSo there are two different numbers to keep apart: how many places the array has (its length, 5), and how many of them are in use (the count, 2). The rest of the module depends on both.\n\n### Making room: a bigger array\n\nSpare room runs out too. When the array is full, the way out is a new, bigger array: create it, copy each value to the same index in the new one, and from then on use the new array:\n\n```java\nimport java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] songs = {\"Morning Rain\", \"Night Train\", \"Paper Boats\"};\n        System.out.println(Arrays.toString(songs));\n\n        String[] bigger = new String[songs.length * 2];\n        for (int i = 0; i < songs.length; i++) {\n            bigger[i] = songs[i];\n        }\n        songs = bigger;\n\n        songs[3] = \"Slow River\";\n        System.out.println(Arrays.toString(songs));\n    }\n}\n```\n\n```output\n[Morning Rain, Night Train, Paper Boats]\n[Morning Rain, Night Train, Paper Boats, Slow River, null, null]\n```\n\n- `songs = bigger;` makes the variable `songs` refer to the new array. Nothing refers to the old array of three any more, so the program can't reach it, and Java's garbage collector may free its memory (module 22).\n- The new array is twice as long as the old one: room for three more songs before the next copy is needed.\n- Java has a ready-made method that does the same: `Arrays.copyOf(songs, 6)` creates the new array and copies the values for you. This module writes the loop out, to show the work it does.\n\nCopying takes one assignment per value: an array of a million values means a million assignments. Keep that in mind; the next step shows how a list keeps that cost down.\n\nRoom to spare, a count, and a bigger array when the room runs out: that's all a list needs. The next step puts the three into a class.\n","task":"A knitting club keeps the names of the stitch patterns it has learned in an array, and it has just learned a fourth. Fill in the blanks of `twiceAsLong`, which returns a new array twice as long as `items`, with the same names at the same indexes. The first blank is the new array's length, and the second copies one name. The program prints:\n\n```\n[garter, rib, cable, lace, null, null]\n6\n```\n","kind":"fill","seed":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] patterns = {\"garter\", \"rib\", \"cable\"};\n        patterns = twiceAsLong(patterns);\n        patterns[3] = \"lace\";\n        System.out.println(Arrays.toString(patterns));\n        System.out.println(patterns.length);\n    }\n\n    public static String[] twiceAsLong(String[] items) {\n        int newLength = [[items.length * 2‖2 * items.length]];\n        String[] bigger = new String[newLength];\n        for (int i = 0; i < items.length; i++) {\n            [[bigger[i] = items[i];]]\n        }\n        return bigger;\n    }\n}\n","solution":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] patterns = {\"garter\", \"rib\", \"cable\"};\n        patterns = twiceAsLong(patterns);\n        patterns[3] = \"lace\";\n        System.out.println(Arrays.toString(patterns));\n        System.out.println(patterns.length);\n    }\n\n    public static String[] twiceAsLong(String[] items) {\n        int newLength = items.length * 2;\n        String[] bigger = new String[newLength];\n        for (int i = 0; i < items.length; i++) {\n            bigger[i] = items[i];\n        }\n        return bigger;\n    }\n}\n","hints":["The old array's length is `items.length`, and the new one is twice that: `items.length * 2`.","Each round of the loop copies the name at index `i` to the same index of the new array: `bigger[i] = items[i];`."],"tests":[{"name":"Output","stdin":"","expect":"[garter, rib, cable, lace, null, null]\n6","hidden":false}],"require":[],"forbid":[],"more":[{"task":"A pottery class keeps its sign-up sheet in an array with a count, as in this step. What does this program print?\n","kind":"predict","seed":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] names = new String[4];\n        int count = 0;\n        names[count] = \"Ines\";\n        count++;\n        names[count] = \"Kofi\";\n        count++;\n        System.out.println(count + \" of \" + names.length);\n        System.out.println(Arrays.toString(names));\n\n        String[] longer = new String[names.length + 2];\n        for (int i = 0; i < count; i++) {\n            longer[i] = names[i];\n        }\n        names = longer;\n        names[count] = \"Lena\";\n        count++;\n        System.out.println(count + \" of \" + names.length + \": \" + names[2] + \", \" + names[3]);\n    }\n}\n","solution":"import java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        String[] names = new String[4];\n        int count = 0;\n        names[count] = \"Ines\";\n        count++;\n        names[count] = \"Kofi\";\n        count++;\n        System.out.println(count + \" of \" + names.length);\n        System.out.println(Arrays.toString(names));\n\n        String[] longer = new String[names.length + 2];\n        for (int i = 0; i < count; i++) {\n            longer[i] = names[i];\n        }\n        names = longer;\n        names[count] = \"Lena\";\n        count++;\n        System.out.println(count + \" of \" + names.length + \": \" + names[2] + \", \" + names[3]);\n    }\n}\n","hints":["The count goes up by one for each name, and it's also the index where the next name goes. The array keeps its length of 4, and its unused places print as `null`.","The longer array has 4 + 2 places. Only the two names are copied, so after Lena goes in at index 2, index 3 is still `null`."],"tests":[{"name":"Output","stdin":"","expect":"2 of 4\n[Ines, Kofi, null, null]\n3 of 6: Lena, null","hidden":false}],"require":[],"forbid":[],"lines":["2 of 4","[Ines, Kofi, null, null]","3 of 6: Lena, null"]},{"task":"A solar panel reports how many watt-hours it made each day. The program reads the reports, one whole number per line, until the line `end`. Keep them in an `int` array that starts with room for 2 reports. Only when a report doesn't fit, create an array twice as long, copy the reports into it, and go on with the new array. No ArrayList.\n\nAfter `end`, print how many reports were read and the length of the array, as shown, and then the reports in the order they came, on one line, each followed by a space. For the input shown, the program prints:\n\n```\n5 reports in an array of length 8\n410 380 455 120 390\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int[] reports = new int[2];\n        int count = 0;\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.equals(\"end\")) {\n                break;\n            }\n            if (count == reports.length) {\n                int[] bigger = new int[reports.length * 2];\n                for (int i = 0; i < count; i++) {\n                    bigger[i] = reports[i];\n                }\n                reports = bigger;\n            }\n            reports[count] = Integer.parseInt(line);\n            count++;\n        }\n        System.out.println(count + \" reports in an array of length \" + reports.length);\n        for (int i = 0; i < count; i++) {\n            System.out.print(reports[i] + \" \");\n        }\n        System.out.println();\n    }\n}\n","hints":["Before the loop: `int[] reports = new int[2];` and `int count = 0;`. For each report, `reports[count] = Integer.parseInt(line);` and then `count++;`.","Just before storing a report, check `if (count == reports.length)`: then the array is full. Create `int[] bigger = new int[reports.length * 2];`, copy the reports in a loop, and set `reports = bigger;`."],"tests":[{"name":"Test 1","stdin":"410\n380\n455\n120\n390\nend\n","expect":"5 reports in an array of length 8\n410 380 455 120 390","hidden":false},{"name":"Test 2","stdin":"200\n300\nend\n","expect":"2 reports in an array of length 2\n200 300","hidden":true},{"name":"Test 3","stdin":"end\n","expect":"0 reports in an array of length 2","hidden":true},{"name":"Test 4","stdin":"1\n2\n3\n4\n5\n6\n7\n8\n9\nend\n","expect":"9 reports in an array of length 16\n1 2 3 4 5 6 7 8 9","hidden":true}],"require":[{"pattern":"\\bnew\\s+int\\s*\\[","message":"Keep the reports in an `int` array that starts as `new int[2]`, and replace it with one twice as long when it's full."}],"forbid":[{"pattern":"\\b(ArrayList|List|LinkedList)\\b","message":"Do it with an `int` array, without a list: growing the array is the point."}]}]}