Collections and Generics
Java ArrayList, HashMap, generics vs C arrays and structs
Introduction
In this lesson, you'll learn about collections and generics in Java. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In C, you're familiar with java arraylist, hashmap, generics vs c arrays and structs.
Java has its own approach to java arraylist, hashmap, generics vs c arrays and structs, which we'll explore step by step.
The Java Way
Let's see how Java handles this concept. Here's a typical example:
import java.util.*;
// ArrayList<T> — dynamic array (like C's realloc but automatic)
List<Integer> nums = new ArrayList<>();
nums.add(1); nums.add(2); nums.add(3);
System.out.println(nums.get(2)); // 3
System.out.println(nums.size()); // 3
// Type-safe — compiler error if wrong type:
// nums.add("hello"); // ERROR
// HashMap<K,V> — like C's linear search, but O(1) average
Map<String, Integer> map = new HashMap<>();
map.put("alice", 30);
map.put("bob", 25);
System.out.println(map.get("alice")); // 30
System.out.println(map.containsKey("bob")); // true
map.remove("bob");
// Iteration
for (Map.Entry<String,Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
// HashSet<T> — unique elements
Set<String> seen = new HashSet<>();
seen.add("a"); seen.add("a"); // dedup
System.out.println(seen.size()); // 1
// Generic method
public static <T extends Comparable<T>> T max(List<T> list) {
return Collections.max(list);
}Comparing to C
Here's how you might have written similar code in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Fixed-size array
int nums[5] = {1, 2, 3, 4, 5};
printf("%d\n", nums[2]); // 3
printf("%d\n", sizeof(nums)/sizeof(nums[0])); // 5
// Dynamic array (manual resizing)
int *dynArr = malloc(10 * sizeof(int));
dynArr[0] = 42;
dynArr = realloc(dynArr, 20 * sizeof(int)); // grow
free(dynArr);
// Simple key-value (linear search — no hash map)
struct KV { char key[50]; int value; };
struct KV store[100];
int count = 0;
void put(const char *k, int v) {
strcpy(store[count].key, k);
store[count++].value = v;
}
int get(const char *k) {
for (int i = 0; i < count; i++)
if (strcmp(store[i].key, k) == 0) return store[i].value;
return -1;
}You may be used to different syntax or behavior.
ArrayList<T> replaces C's manual realloc pattern — auto-resizes, type-safe
You may be used to different syntax or behavior.
HashMap<K,V> provides O(1) average lookups vs C's O(n) linear search
You may be used to different syntax or behavior.
Java generics are compile-time — wrong type is a compile error, not a runtime crash
You may be used to different syntax or behavior.
Collections.sort/min/max work on any Comparable — C needs manual comparator functions
You may be used to different syntax or behavior.
No manual memory management — GC handles ArrayList and HashMap memory
Step-by-Step Breakdown
1. ArrayList vs C Dynamic Array
ArrayList manages its own memory. add() appends, get(i) accesses. No bounds checking in C; Java throws ArrayIndexOutOfBoundsException.
int *arr = malloc(n * sizeof(int));
arr[i] = v;
arr = realloc(arr, 2*n*sizeof(int));List<Integer> list = new ArrayList<>();
list.add(42); // append
list.get(0); // random access
list.set(0, 99); // update
list.remove(0); // remove by index2. HashMap vs Linear Search
HashMap gives O(1) average lookups using hashing. Much faster than C's typical array-of-structs approach for key-value data.
// C: O(n) linear scan
for (int i=0; i<n; i++)
if (strcmp(store[i].key, k)==0) return store[i].val;Map<String,Integer> map = new HashMap<>();
map.put("key", 42); // O(1)
int v = map.get("key"); // O(1)
map.getOrDefault("x", 0); // safe get3. Generics for Type Safety
Java generics catch type errors at compile time. Without generics (raw types), errors would surface as ClassCastException at runtime.
// C: void* array — no type safety
void *arr[100];
arr[0] = &someInt; arr[1] = "string"; // both validList<Integer> nums = new ArrayList<>();
nums.add(42); // ok
// nums.add("hi"); // compile error — type safety!4. Iterating Collections
Enhanced for-each loop works on any Iterable. For maps, iterate entries, keys, or values.
for (int i=0; i<n; i++) { printf("%d\n", arr[i]); }// List
for (int n : nums) System.out.println(n);
// Map keys
for (String k : map.keySet()) System.out.println(k);
// Map entries
for (var e : map.entrySet())
System.out.println(e.getKey() + "=" + e.getValue());Common Mistakes
When coming from C, developers often make these mistakes:
- ArrayList<T> replaces C's manual realloc pattern — auto-resizes, type-safe
- HashMap<K,V> provides O(1) average lookups vs C's O(n) linear search
- Java generics are compile-time — wrong type is a compile error, not a runtime crash
Key Takeaways
- ArrayList<T> = auto-resizing type-safe dynamic array; no malloc/realloc/free
- HashMap<K,V> = O(1) key-value store; containsKey/put/get/remove
- Generics catch type errors at compile time — no void* or ClassCastException at runtime
- for-each loop works on any Iterable; map.entrySet() for key-value iteration