Arrays, Collections, and Generics — the fundamental toolkits for storing and organising groups of data in Java. If you have ever needed to keep a list of scores, a set of unique user IDs, or a mapping of employee names to their salaries, these structures are your answer. For the 1Z0-829 exam, understanding when to use an array versus a List, or a Set versus a Map, and how to make them type-safe with generics, is tested heavily — expect at least 5-7 questions on these topics.
Jump to a section
A simple way to picture Arrays, Collections, and Generics
A head chef runs a busy restaurant kitchen. She has different storage systems for different ingredients. A fixed-size, metal tray with 10 slots is her "array" — she knows exactly how many slots it has, and she can only hold one ingredient type per tray (say, only sliced tomatoes). If she needs more slots, she must throw the tray away and buy a bigger one. That's an array: fast to access any slot, but rigid.
Her "collection" is like a set of stackable, flexible plastic bins. She can add or remove bins as needed. One bin might hold all the chopped onions (a "List" — order matters, duplicates allowed). Another bin might be a "Set" — a single compartment where each ingredient can only appear once (like unique spice blends). The "Map" is her recipe binder, where each recipe name (key) points to a list of ingredients (value). She can quickly look up "Tiramisu" and find the ingredients, without caring about the order of recipes.
"Generics" are the labels she puts on each bin. Instead of a bin labelled "Stuff", she labels it "Chopped Onions" or "Sliced Mushrooms". This prevents her from accidentally putting carrots into the mushroom bin. Generics tell the kitchen (the compiler) what type of ingredient each bin is supposed to hold, catching mistakes before the dish is served (before the program runs).
The head chef's "Stack" is the pile of clean plates by the pass. She always takes the top plate first (Last-In, First-Out). Her "Queue" is the line of waiters waiting for their orders — first waiter in gets served first (First-In, First-Out). These are specialised collections, each with a specific rule for adding and removing items.
Without these organised systems, the kitchen would be chaos: ingredients would go missing, orders would be wrong, and the chef would have no way to scale up for a busy Saturday night dinner service.
Let's start with the simplest container: the array. An array is a fixed-size, ordered container that holds elements of a single type. You declare it with square brackets, like int[] scores = new int[5]; This creates a box that can hold exactly five integers, stored in positions 0 through 4 (Java is zero-indexed). To put a value in, you write scores[0] = 95; To read a value, you write int firstScore = scores[0]; Arrays are lightning-fast for accessing any position because the memory location is calculated directly. The big downside: you cannot add or remove slots after creation. If you need six scores, you must create a new, larger array and copy everything over. That is clumsy and error-prone.
This is where the Collections Framework comes in. The Java Collections Framework is a unified architecture for storing and manipulating groups of objects. It provides interfaces (contracts) and classes (implementations) that solve the limitations of arrays. The four main interface families you must know for the exam are List, Set, Map, and Queue (which includes Deque and Stack by inheritance). A List is an ordered collection that allows duplicates. You can insert, remove, and access elements by index, and it grows and shrinks dynamically. Common implementations are ArrayList (backed by a resizable array) and LinkedList (backed by linked nodes). A Set is an unordered collection that forbids duplicates. It is perfect for ensuring uniqueness — for example, a set of all registered email addresses. HashSets use hash tables for fast lookup; TreeSets keep elements sorted. A Map is not technically a Collection (it does not extend the Collection interface), but it is part of the framework. It stores key-value pairs, where each key is unique. You look up a value by its key, like a dictionary. HashMap is the most common implementation. Queue and Deque (double-ended queue) represent collections for holding elements prior to processing. A typical Queue processes elements in FIFO (First-In, First-Out) order, while a Deque allows insertion and removal at both ends. Stack is a legacy class that represents a LIFO (Last-In, First-Out) stack, but Deque is now preferred.
Now for Generics. Before generics were introduced in Java 5, you could put any type of object into a collection, and you had to cast it back when you retrieved it. This was unsafe and led to runtime ClassCastExceptions. Generics solve this by allowing you to specify the type of elements a collection can hold at compile time. You write ArrayList<String> names = new ArrayList<>(); The angle brackets <String> are the generic type parameter. They tell the compiler: "This ArrayList will only hold String objects" . If you try to add an Integer to it, the compiler will reject it with an error. This catches bugs before you even run the programme.
The diamond operator <> was introduced in Java 7 to reduce redundancy. On the left side of an assignment, you write the full generic type (ArrayList<String>). On the right side, you can just write the diamond (new ArrayList<>()), and the compiler infers the type from the left side. The exam loves testing whether you know where the diamond is allowed and where it is not.
Generic classes can be defined with type parameters that have bounds. For example, List<? extends Number> means a list that holds elements of some unknown type that is a subtype of Number (like Integer or Double). This is called a wildcard with an upper bound. Similarly, List<? super Integer> means a list that can hold Integer or any supertype of Integer (like Number or Object). These wildcards are critical for writing flexible, reusable methods. The PECS rule (Producer Extends, Consumer Super) helps you decide which to use: if you are only reading items from a structure, use ? extends T; if you are only inserting items, use ? super T.
Let's walk through a concrete example. Imagine you are building a library system. You need to store a list of books. With an array, you would create Book[] books = new Book[100]; If the library grows to 101 books, you have a problem. With a List, you can add a 101st book dynamically: List<Book> books = new ArrayList<>(); books.add(new Book()); You also want to ensure no duplicate ISBNs are added. For that, you use a Set: Set<String> isbns = new HashSet<>(); And you want to look up a book by its shelf location code. That is a Map: Map<String, Book> shelfLocation = new HashMap<>(); Each collection solves a distinct problem.
For the exam, you must memorise the key characteristics of each collection type, when to use which, and the syntax of generics including wildcards and the diamond operator. You will also be tested on utility methods from the Collections and Arrays classes, such as Collections.sort(), Collections.binarySearch(), Arrays.asList(), and Arrays.sort(). These are static helper methods that operate on collections and arrays. For instance, Arrays.asList() returns a fixed-size List backed by the array — you cannot add or remove elements from this list, but you can change existing elements. That's a classic exam trap.
Finally, remember that raw types (using a generic class without type parameters, e.g., List list = new ArrayList(); ) are allowed but discouraged. They generate compiler warnings and can cause runtime cast exceptions. The exam expects you to know that raw types exist solely for backward compatibility with pre-Java 5 code.
Step 1: Recognise the need for multiple objects
Identify that your application needs to store more than one piece of related data. For example, a list of customer names or a mapping of product IDs to products. This is the fundamental reason to use arrays or collections.
Step 2: Decide between array and collection
If the number of elements is fixed and known at compile time (e.g., days of the week), use an array for simplicity and slight performance gain. Otherwise, choose a collection from the Java Collections Framework. For most applications, collections are the correct choice.
Step 3: Choose the correct collection type
Select the interface based on your requirements. If you need ordered, indexed access with possible duplicates, use List (e.g., ArrayList). If you need uniqueness, use Set (e.g., HashSet). If you need key-value lookups, use Map (e.g., HashMap). If you need LIFO or FIFO processing, use Deque (e.g., ArrayDeque).
Step 4: Apply generics for type safety
Declare the collection with a generic type parameter to specify what type of objects it will hold, e.g., `List<String> names = new ArrayList<>();`. This prevents accidentally adding objects of the wrong type and eliminates the need for casting when retrieving elements.
Step 5: Add and manipulate elements
Use the appropriate methods for your chosen collection. For List: `add()`, `remove()`, `get(index)`, `set(index, value)`. For Set: `add()`, `remove()`, `contains()`. For Map: `put(key, value)`, `get(key)`, `remove(key)`, `containsKey()`. For Deque: `push()`, `pop()`, `offer()`, `poll()`.
Step 6: Use utility methods for common operations
Leverage static methods from Collections (e.g., `Collections.sort()`, `Collections.binarySearch()`, `Collections.reverse()`) and Arrays (e.g., `Arrays.asList()`, `Arrays.sort()`) to perform sorting, searching, and conversions without writing custom loops.
Step 7: Iterate or stream for bulk processing
Use for-each loops or streams (Java 8+) to process all elements. For example, `for (String name : names) { System.out.println(name); }` or `names.stream().filter(n -> n.startsWith("A")).collect(Collectors.toList());`. This step is about efficiently working with the data once stored.
A junior developer at a retail company is tasked with building a feature to manage the company's product inventory. The system needs to store thousands of products, each with a unique SKU code, a name, a price, and a quantity in stock. The developer starts by defining the data structures.
Step one: representing a product. The developer creates a Java class Product with fields for SKU (String), name (String), price (double), and quantity (int). Now, she needs to store all products in memory while the application is running.
She considers using an array of Product objects, like Product[] inventory = new Product[10000]; But the company's product catalogue changes daily — new products are added, old ones are discontinued. An array's fixed size is a nightmare. She would have to constantly check if the array is full and create a larger one, copying the data. That is brittle and slow. Instead, she chooses an ArrayList<Product>. This list grows automatically. She writes List<Product> inventory = new ArrayList<>(); and calls inventory.add(product); for each new product. When a product is discontinued, she calls inventory.remove(product); and the list shrinks. The order of products matches the order she adds them, which is useful for display.
Step two: ensuring SKU uniqueness. The inventory list accidentally allows two different products with the same SKU if someone writes buggy code. To prevent this, she creates a secondary data structure: a Set<String> skuSet = new HashSet<>();. Every time she adds a new product, she first checks if (!skuSet.add(product.getSku())) { // handle duplicate }. The Set's add() method returns false if the element already exists. This enforces uniqueness at the application level.
Step three: looking up a product by SKU quickly. Currently, to find a product, she has to loop through the entire inventory list — O(n) time, which is slow for thousands of products. She needs O(1) lookup. She creates a Map<String, Product> skuToProduct = new HashMap<>();. When a product is added, she also does skuToProduct.put(product.getSku(), product);. Now, retrieving a product is instant: Product p = skuToProduct.get(skuCode);.
Step four: sorting products by price for a sale. She uses Collections.sort(inventory, Comparator.comparingDouble(Product::getPrice)); This rearranges the list. To search for a specific price, she could then use Collections.binarySearch() on the sorted list.
Step five: generating report data. She needs to quickly find all products whose quantity is below a restock threshold. She uses a Java Stream (though that is a later exam objective) to filter the list: inventory.stream().filter(p -> p.getQuantity() < 10).collect(Collectors.toList());. But the foundation — the ArrayList — makes this possible.
Step six: handling future changes. The Product class becomes generic. She creates a ProductCategory<T> where T is the type of the category-specific data (e.g., ProductCategory<ElectronicsExtra> or ProductCategory<ClothingExtra>). This uses generics to provide type safety for different product types without creating separate classes for each.
A senior developer reviews her code and spots a potential issue: she is using ArrayList in a method signature public void process(List<Product> items) which is flexible, but she also has a method public void processSpecial(Map<String, ? extends Product> specialMap) to accept maps with product subtypes. This demonstrates her understanding of generic wildcards for API flexibility.
By the end of the sprint, the developer has built a robust inventory management system using Lists for ordered product storage, Sets for uniqueness, Maps for fast lookups, and generics for type safety. This is exactly the kind of real-world problem-solving that the 1Z0-829 exam tests conceptually — not rote memorisation of class hierarchy, but knowing which tool fits which job.
The 1Z0-829 exam tests 5.1 (Arrays, Collections, and Generics) with about 6-8 questions. The examiners love to set traps using subtle distinctions between collections that look similar but behave differently. Here is what you must master.
List vs Set vs Map: You must know the core contract differences. A List allows duplicates and maintains insertion order. A Set forbids duplicates. A Map stores key-value pairs. Questions often show code that adds duplicate elements and asks what the size will be. For a HashSet, the second add simply returns false and does not change the size. For an ArrayList, the size increments even with a duplicate.
ArrayList vs LinkedList: The exam tests performance characteristics. ArrayList gives O(1) random access (get by index), but O(n) insertion in the middle. LinkedList gives O(n) random access but O(1) insertion at beginning or middle (if you have the node reference). A typical question: "Which List implementation should you use if you frequently add elements at the beginning?" Answer: LinkedList.
HashMap vs TreeMap vs LinkedHashMap: HashMap offers O(1) average-time operations, unordered. TreeMap keeps keys sorted (by Comparable or Comparator). LinkedHashMap maintains insertion order. The exam asks you to predict the iteration order after adding elements. A common trap: using a HashMap and expecting insertion order — you cannot rely on it.
Generics and Wildcards: This is a heavily tested area. You must encode the PECS rule (Producer Extends, Consumer Super). Questions show method signatures like void addAll(List<? super Number> list) and ask what types of lists you can pass. The answer: you can pass a List<Number>, List<Object>, or List<Serializable>, but NOT a List<Integer>. Conversely, void printAll(List<? extends Number> list) accepts List<Integer>, List<Double>, List<Number>, but NOT List<Object>.
The diamond operator: The exam frequently tests where the diamond <> is valid. It is allowed on the right side of an assignment when the left side provides the type context. It is NOT allowed in anonymous classes (before Java 9 — on the exam, treat anonymous classes as requiring explicit type on the right). Trap: List<String> list = new ArrayList<>(); is valid. List<> list = new ArrayList<String>(); is NOT valid.
Raw types: Questions on raw types appear at least once. Example: List raw = new ArrayList(); raw.add("hello"); String s = (String) raw.get(0); This compiles with unchecked warnings but works. The exam asks why raw types are bad: they bypass compile-time type checking and can cause ClassCastException at runtime.
Collections utility methods: Collections.sort(List<T> list) requires that T implements Comparable. If T does not, the compiler throws an error. Collections.binarySearch() requires the list to be sorted first, otherwise the result is undefined. Arrays.asList() returns a fixed-size list backed by the original array — you can call set(index, value) but not add() or remove(). The exam will test this with an UnsupportedOperationException trap.
Autoboxing with collections: When adding primitives to a collection (e.g., List<Integer>), Java automatically boxes them (converts int to Integer). The exam tests your knowledge that List<Integer> cannot hold a primitive int directly — autoboxing happens implicitly. However, comparing Integer objects with == works only for values in the range -128 to 127 (the integer cache). Outside that range, == compares references, not values. Use .equals() for value comparison.
Null elements: Most collections allow null elements. HashSet allows one null (since HashMap allows one null key). ArrayList allows multiple nulls. TreeSet does NOT allow null — it will throw NullPointerException on insertion because it needs to compare elements for sorting. This is a common exam trap.
Legacy classes: Stack and Vector are legacy and synchronised. They are rarely used today. The exam may ask which modern counterpart replaces them (Deque for Stack, ArrayList for Vector).
To prepare, drill with flashcards for: List vs Set vs Map characteristics, ArrayList vs LinkedList, HashMap vs TreeMap, wildcard syntax (extends/super), diamond operator rules, Collections utility methods, and autoboxing pitfalls. Practice reading code snippets and predicting output. The majority of questions are "what is the output of this code" style.
An array is a fixed-size, low-level container; a List is a resizable, high-level collection that is almost always preferred for ordered data.
A Set guarantees uniqueness; use HashSet for fast lookups, TreeSet for sorted order, and LinkedHashSet for insertion order.
A Map stores key-value pairs with unique keys; HashMap offers O(1) average performance, TreeMap keeps keys sorted, LinkedHashMap preserves insertion order.
Generics provide compile-time type safety, eliminating the need for casting and preventing ClassCastException with collections.
The diamond operator `<>` must be used on the right side of an assignment only when the left side provides sufficient type context.
Use `? extends T` when you only read items from a structure (Producer), and `? super T` when you only insert items (Consumer) — this is the PECS rule.
`Arrays.asList()` returns a fixed-size list backed by the original array; you cannot add or remove elements from it.
Always use `.equals()` to compare the values of Integer objects outside the -128 to 127 cache range, never `==`.
TreeSet and TreeMap do not allow null elements because they need to compare elements for ordering.
Raw types (List without generics) are allowed for backward compatibility but should never be used in new code due to lack of type safety.
These come up on the exam all the time. Here's how to tell them apart.
Array
Fixed size: cannot add or remove elements after creation
Can hold primitives directly (int[], char[])
Part of the Java language, not a class; uses [] syntax
ArrayList
Resizable: dynamically grows and shrinks as elements are added/removed
Cannot hold primitives directly; uses wrapper classes with autoboxing
Part of the java.util package; a generic class with methods like add(), remove()
HashSet
Uses hash table; offers O(1) average-time for add, remove, contains
Does not maintain any order; iteration order is unpredictable
Allows null element (one null)
TreeSet
Uses a Red-Black tree; offers O(log n) for add, remove, contains
Maintains elements in sorted order (natural or Comparator-defined)
Does not allow null elements (throws NullPointerException)
List
Allows duplicate elements
Maintains insertion order (in typical implementations like ArrayList)
Elements can be accessed by index (position)
Set
Forbids duplicate elements (uniqueness is guaranteed)
Order is not guaranteed (except in LinkedHashSet and TreeSet)
No index-based access; must iterate or use contains() to find elements
HashMap
Uses hash table; O(1) average-time for get and put
Does not guarantee any order of keys
Allows one null key and multiple null values
TreeMap
Uses Red-Black tree; O(log n) for get and put
Maintains keys in sorted order (natural or Comparator-defined)
Does not allow null keys (throws NullPointerException)
Mistake
Arrays and ArrayLists are essentially the same thing, just ArrayLists have more features.
Correct
Arrays are fixed-size, built-in data structures; ArrayLists are resizable, part of the Collections Framework. An array is a primitive concept; ArrayList is a generic class that uses an array internally. You cannot add or remove elements from an array; ArrayLists can grow and shrink.
Because both use square brackets in declaration (int[] vs ArrayList<>) and both store ordered elements, beginners conflate them. The fact that ArrayLists are implemented using arrays internally reinforces the confusion.
Mistake
You can put any type into a generic collection if you use the diamond operator.
Correct
The diamond operator `<>` infers the type from the left side. If the left side says `List<String>`, the diamond creates an ArrayList of String, not Object. You cannot add an Integer to it. The diamond does not mean 'any type'.
The name 'diamond' sounds generic and vague. Beginners think it means 'whatever type fits', when it actually means 'the same type as declared on the left'.
Mistake
A HashSet maintains the order in which elements were inserted.
Correct
HashSet does NOT guarantee any order. It uses a hash table, so iteration order can appear random and can change when the set is resized. If you need insertion order, use LinkedHashSet. If you need sorted order, use TreeSet.
Some collections like ArrayList preserve insertion order, so beginners assume all collections do. The name 'HashSet' does not hint at ordering behaviour, making it a common trap.
Mistake
Using a raw type like `List list = new ArrayList();` is fine because it compiles without error.
Correct
Raw types compile with unchecked warnings and bypass generic type safety. They can lead to ClassCastException at runtime. They exist only for backward compatibility with pre-Java 5 code. The compiler emits warnings, and the exam considers them bad practice.
Beginners see that the code compiles and runs, so they assume it's acceptable. They don't understand that the warnings are serious indicators of potential runtime bugs.
Mistake
The `Arrays.asList()` method returns an ArrayList, so you can add and remove elements freely.
Correct
`Arrays.asList()` returns a fixed-size List backed by the original array. You cannot add or remove elements — calling `add()` or `remove()` throws UnsupportedOperationException. You can only replace existing elements using `set()`.
The method name suggests it creates an ArrayList, but it actually returns a private inner class of Arrays that wraps the array. Beginners don't read the Javadoc and assume it behaves like a normal ArrayList.
Mistake
When you store an int in an Integer collection, you can compare values using `==` and it always works.
Correct
Autoboxing converts int to Integer. Integer objects are cached for values between -128 and 127. For values outside this range, `==` compares object references, not values. You must use `.equals()` for safe value comparison.
Beginners are used to comparing primitives with `==`. Autoboxing makes Integer look like a primitive, so they forget that Integer is an object. The integer cache masks the problem for common small values.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
A List is an ordered collection that allows duplicate elements. A Set is an unordered collection that forbids duplicates. Use a List when you care about the position of elements, and a Set when you need to ensure uniqueness.
No, collections cannot hold primitive types directly. You must use wrapper classes like Integer, Double, or Boolean. Java automatically converts between primitives and their wrappers through autoboxing and unboxing.
The diamond operator allows the compiler to infer the type of a generic class from the context. For example, in `List<String> list = new ArrayList<>();`, the compiler knows the ArrayList is of type String. It reduces redundancy and was introduced in Java 7.
HashSet uses a hash table for storage, which does not maintain any specific order. If you need insertion order, use LinkedHashSet. If you need sorted order, use TreeSet.
ArrayList is backed by a resizable array and provides O(1) random access by index, but O(n) time for inserting or removing elements in the middle. LinkedList is backed by a doubly-linked list and provides O(1) insertion/removal at both ends, but O(n) random access. Choose based on your access and modification patterns.
Use a Map when you need to look up a value by a unique key quickly (O(1) on average with HashMap). For example, mapping a customer ID to a customer object. Two separate Lists would require linear search to find the matching key, which is slow for large datasets.
A raw type is a generic class used without type parameters, e.g., `List list = new ArrayList();`. It bypasses compile-time type checking, allowing you to add any type of object. This forces you to use casts when retrieving elements, potentially causing ClassCastException at runtime. Raw types exist only for backward compatibility.
You've finished Arrays, Collections, and Generics. Continue through the 1Z0-829 study guide to build a complete picture of the exam.
Done with this chapter?