Collectable Types and Collections
This is a deeper review on data structures specific to Java. Many of these Data Structures are referred to as Collections. Using Collections requires deeper understand of Objects and the Generic Type.
Arrays, ArrayList, 2D Arrays
Most “Data Structures” conversations begin with Arrays, which are built into most Computer Programming Languages. College Board has CSA Units 6-8 which discuss Arrays, ArrayLists, and 2-Dimensional Arrays.
Arrays, 2D Arrays, and ArrayLists are important data structures in computer science, and they are the subject of two FRQs in each AP Computer Science A exam. Here are some types of FRQs that may focus on these topics:
- Array/ArrayList implementation: You may be asked to implement an Array or ArrayList, including methods to add, remove, and access elements.
- Array/ArrayList traversal: You may be given an Array or ArrayList and asked to traverse it, perform operations on each element, and/or modify the array or list.
- Array/ArrayList searching and sorting: You may be asked to implement or modify code to search for an element in an array or list, or to sort the elements of an array or list.
- 2D Arrays or Multi-dimensional arrays: You may be asked to implement or modify code that uses a multi-dimensional array, and to perform operations on elements of the array.
- ArrayList vs. Array: You may be asked to compare and contrast the characteristics of ArrayLists and Arrays, and to explain when it is appropriate to use one data structure over the other.
- Big-O complexity: You may be asked to analyze the time and space complexity of algorithms that use Arrays or ArrayLists, and to compare the efficiency of different algorithms.
Collection Framework in Java
A deeper dive into Data Structures continues with Linked Lists (LL) which are the foundation for Stacks and Queues, which we have used. Java has implemented a Collection framework that has established common methods to assist in using many of these Data Structures.
Queue<String> queue = new LinkedList<>(); // Queue interface uses LL implementation
queue.add("John");
queue.add("Jane");
queue.add("Bob");
ULTRA CHAOS MODE: Collections Multiverse Collapse
You are no longer just a Data Warden. You are the Last Compiler Knight. The Java multiverse is collapsing because corrupted data structures escaped into runtime.
World Events (each class period, roll one)
Memory Leak Moon- all healing is reducedInfinite Loop Eclipse- if a loop has no clear stop condition, instant party wipeNull Storm- one random entity becomesnulland must be guardedConcurrency Rift- random event order mutation attempt (must explain why Queue order matters)
Factions (student roles)
Architect- owns class design and inheritanceRuntime Medic- handles null checks / guard clausesBalance Wizard- tunes stats to avoid trivial winsChronomancer- explains complexity tradeoffs after each phase
XP Economy
- Zone clear: 30 XP
- Chaos card survive: +20 XP
- Perfect debug explanation: +25 XP
- Boss phase clear (x3 phases): +35 XP each
- No compile errors in final raid: +40 XP flawless bonus
Mythic Relics
O(1) CrownGeneric SigilQueue of DestinyComparator BladeNullproof Aegis
Raid Campaign
- Zone Alpha: Array Wastes - capacity pain and indexing precision
- Zone Beta: Queue Canyon - strict FIFO survival under chaos
- Zone Gamma: Generic Forge - type-safe crafting of custom objects
- Zone Delta: Comparator Colosseum - ranking, tie-breaks, fairness
- Final Event: NullPointer Titan - multi-phase roguelike simulation
Queue<String> queue = new LinkedList<>(); // Queue interface uses LL implementation
queue.add("John");
queue.add("Jane");
queue.add("Bob");
// Collections has a toArray convertion which is a copy of the queue
Object[] arr = queue.toArray();
// Empty queue
System.out.println("Remove each element from the LinkedList Queue of size: " + queue.size());
while (queue.size() > 0) // Interate while size
System.out.println(queue.remove());
System.out.println("Is Queue empty: " + queue.isEmpty());
// Blank line in console
System.out.println();
// Iterate of array
System.out.println("Iterate over the Array as the copy still has size: " + arr.length);
for (Object a : arr) // Type is Object from convertion
System.out.println(a);
Remove each element from the LinkedList Queue of size: 3
John
Jane
Bob
Is Queue empty: true
Iterate over the Array as the copy still has size: 3
John
Jane
Bob
Collectable
The purpose of this “class” definition is to show how we can combine any Data Type into a super class.
- This class is abstract, meaning it can not used unless extended.
- The keyword interface is used to ensure people define “interface” in their implementation. This can be used for things like sorting and searching information within the class.
- Every object in Java is inherited from Data Type “Object”. This is shown in toString() method @Overrides below. The toString() method has a prototype implementation in “Object”. Each extended class that @Overrides toString() and can be used to create a string representation of its “Object”.
/* This is wrapper class...
Objective would be to push more functionality into this Class to enforce consistent definition
*/
public abstract class Collectable implements Comparable <Collectable> {
public final String masterType = "Collectable";
private String type; // extender should define their data type
// enumerated interface
public interface KeyTypes {
String name();
}
protected abstract KeyTypes getKey(); // this method helps force usage of KeyTypes
// getter
public String getMasterType() {
return masterType;
}
// getter
public String getType() {
return type;
}
// setter
public void setType(String type) {
this.type = type;
}
// this method is used to establish key order
public abstract String toString();
// this method is used to compare toString of objects
public int compareTo(Collectable obj) {
return this.toString().compareTo(obj.toString());
}
// static print method used by extended classes
public static void print(Collectable[] objs) {
// print 'Object' properties
System.out.println(objs.getClass() + " " + objs.length);
// print 'Collectable' properties
if (objs.length > 0) {
Collectable obj = objs[0]; // Look at properties of 1st element
System.out.println(
obj.getMasterType() + ": " +
obj.getType() +
" listed by " +
obj.getKey());
}
// print "Collectable: Objects'
for(Object o : objs) // observe that type is Opaque
System.out.println(o);
System.out.println();
}
}
Alphabet extends Collectable
This class is used to store the alphabet.
- Implements interface KeyType which is used in toString method of Alphabet object
- Overrides methods in abstract class Collectable, which includes class Object.
public class Alphabet extends Collectable {
// Class data
public static KeyTypes key = KeyType.title; // static initializer
public static void setOrder(KeyTypes key) {Alphabet.key = key;}
public enum KeyType implements KeyTypes {title, letter}
private static final int size = 26; // constant used in data initialization
// Instance data
private final char letter;
/*
* single letter object
*/
public Alphabet(char letter)
{
this.setType("Alphabet");
this.letter = letter;
}
/* 'Collectable' requires getKey to help enforce KeyTypes usage */
@Override
protected KeyTypes getKey() { return Alphabet.key; }
/* 'Collectable' requires toString override
* toString provides data based off of Static Key setting
*/
@Override
public String toString()
{
String output="";
if (KeyType.letter.equals(this.getKey())) {
output += this.letter;
} else {
output += super.getType() + ": " + this.letter;
}
return output;
}
// Test data initializer for upper case Alphabet
public static Alphabet[] alphabetData()
{
Alphabet[] alphabet = new Alphabet[Alphabet.size];
for (int i = 0; i < Alphabet.size; i++)
{
alphabet[i] = new Alphabet( (char)('A' + i) );
}
return alphabet;
}
/*
* main to test Animal class
*/
public static void main(String[] args)
{
// Inheritance Hierarchy
Alphabet[] objs = alphabetData();
// print with title
Alphabet.setOrder(KeyType.title);
Alphabet.print(objs);
// print letter only
Alphabet.setOrder(KeyType.letter);
Alphabet.print(objs);
}
}
Alphabet.main(null);
class [LREPL.$JShell$24$Alphabet; 26
Collectable: Alphabet listed by title
Alphabet: A
Alphabet: B
Alphabet: C
Alphabet: D
Alphabet: E
Alphabet: F
Alphabet: G
Alphabet: H
Alphabet: I
Alphabet: J
Alphabet: K
Alphabet: L
Alphabet: M
Alphabet: N
Alphabet: O
Alphabet: P
Alphabet: Q
Alphabet: R
Alphabet: S
Alphabet: T
Alphabet: U
Alphabet: V
Alphabet: W
Alphabet: X
Alphabet: Y
Alphabet: Z
class [LREPL.$JShell$24$Alphabet; 26
Collectable: Alphabet listed by letter
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Animal. This class is used to store properties on Animals.
- Extends Collectable.
- Implements interface KeyType with more keys than 1st example, as this Class has more attributes.
- Overrides methods in abstract Collectable, notice that this has more variations of Display.
/*
* Animal class extends Collectable and defines abstract methods
*/
public class Animal extends Collectable {
// Class data
public static KeyTypes key = KeyType.title; // static initializer
public static void setOrder(KeyTypes key) { Animal.key = key; }
public enum KeyType implements KeyTypes {title, name, age, color}
// Instance data
private final String name;
private final int age;
private final String color;
/* constructor
*
*/
public Animal(String name, int age, String color)
{
super.setType("Animal");
this.name = name;
this.age = age;
this.color = color;
}
/* 'Collectable' requires getKey to help enforce KeyTypes usage */
@Override
protected KeyTypes getKey() { return Animal.key; }
/* 'Collectable' requires toString override
* toString provides data based off of Static Key setting
*/
@Override
public String toString()
{
String output="";
if (KeyType.name.equals(this.getKey())) {
output += this.name;
} else if (KeyType.age.equals(this.getKey())) {
output += "00" + this.age;
output = output.substring(output.length() - 2);
} else if (KeyType.color.equals(this.getKey())) {
output += this.color;
} else {
output += super.getType() + ": " + this.name + ", " + this.color + ", " + this.age;
}
return output;
}
// Test data initializer
public static Animal[] animals() {
return new Animal[]{
new Animal("Lion", 8, "Gold"),
new Animal("Pig", 3, "Pink"),
new Animal("Robin", 7, "Red"),
new Animal("Cat", 10, "Black"),
new Animal("Kitty", 1, "Calico"),
new Animal("Dog", 14, "Brown")
};
}
/* main to test Animal class
*
*/
public static void main(String[] args)
{
// Inheritance Hierarchy
Animal[] objs = animals();
// print with title
Animal.setOrder(KeyType.title);
Animal.print(objs);
// convert to Coolection and sort in name order
Animal.setOrder(KeyType.name);
List<Animal> animals = new ArrayList<Animal>(Arrays.asList(objs)); // Array has asList conversion
Collections.sort(animals);
Animal.setOrder(KeyType.title);
for (Animal animal : animals)
System.out.println(animal);
}
}
Animal.main(null);
class [LREPL.$JShell$26$Animal; 6
Collectable: Animal listed by title
Animal: Lion, Gold, 8
Animal: Pig, Pink, 3
Animal: Robin, Red, 7
Animal: Cat, Black, 10
Animal: Kitty, Calico, 1
Animal: Dog, Brown, 14
Animal: Cat, Black, 10
Animal: Dog, Brown, 14
Animal: Kitty, Calico, 1
Animal: Lion, Gold, 8
Animal: Pig, Pink, 3
Animal: Robin, Red, 7
Cupcake. This class is used to store properties of Cupcakes.
- Extends Collectable.
- Implements interface, very similar to previous example.
- Overrides methods in abstract Collectable.
- Though Animals and Cupcakes are very different in real word, properties and management look very similar.
public class Cupcake extends Collectable {
// Class data
public static KeyTypes key = KeyType.title; // static initializer
public static void setOrder(KeyTypes key) {Cupcake.key = key;}
public enum KeyType implements KeyTypes {title, flavor, frosting, sprinkles}
// Instance data
private final String frosting;
private final int sprinkles;
private final String flavor;
// Constructor
Cupcake(String frosting, int sprinkles, String flavor)
{
this.setType("Cupcake");
this.frosting = frosting;
this.sprinkles = sprinkles;
this.flavor = flavor;
}
/* 'Collectable' requires getKey to help enforce KeyTypes usage */
@Override
protected KeyTypes getKey() { return Cupcake.key; }
/* 'Collectable' requires toString override
* toString provides data based off of Static Key setting
*/
@Override
public String toString() {
String output="";
if (KeyType.flavor.equals(this.getKey())) {
output += this.flavor;
} else if (KeyType.frosting.equals(this.getKey())) {
output += this.frosting;
} else if (KeyType.sprinkles.equals(this.getKey())) {
output += "00" + this.sprinkles;
output = output.substring(output.length() - 2);
} else {
output = super.getType() + ": " + this.flavor + ", " + this.frosting + ", " + this.sprinkles;
}
return output;
}
// Test data initializer
public static Cupcake[] cupcakes() {
return new Cupcake[]{
new Cupcake("Red", 4, "Red Velvet"),
new Cupcake("Orange", 5, "Orange"),
new Cupcake("Yellow", 6, "Lemon"),
new Cupcake("Green", 7, "Apple"),
new Cupcake("Blue", 8, "Blueberry"),
new Cupcake("Purple", 9, "Blackberry"),
new Cupcake("Pink", 10, "Strawberry"),
new Cupcake("Tan", 11, "Vanilla"),
new Cupcake("Brown", 12, "Chocolate"),
};
}
public static void main(String[] args)
{
// Inheritance Hierarchy
Cupcake[] objs = cupcakes();
// print with title
Cupcake.setOrder(KeyType.title);
Cupcake.print(objs);
// convert to Coolection and sort in flavor order
Cupcake.setOrder(KeyType.flavor);
List<Cupcake> cupcakes = new ArrayList<Cupcake>(Arrays.asList(objs));
Collections.sort(cupcakes);
Cupcake.setOrder(KeyType.title);
for (Cupcake cupcake : cupcakes)
System.out.println(cupcake);
}
}
Cupcake.main(null);
class [LREPL.$JShell$28$Cupcake; 9
Collectable: Cupcake listed by title
Cupcake: Red Velvet, Red, 4
Cupcake: Orange, Orange, 5
Cupcake: Lemon, Yellow, 6
Cupcake: Apple, Green, 7
Cupcake: Blueberry, Blue, 8
Cupcake: Blackberry, Purple, 9
Cupcake: Strawberry, Pink, 10
Cupcake: Vanilla, Tan, 11
Cupcake: Chocolate, Brown, 12
Cupcake: Apple, Green, 7
Cupcake: Blackberry, Purple, 9
Cupcake: Blueberry, Blue, 8
Cupcake: Chocolate, Brown, 12
Cupcake: Lemon, Yellow, 6
Cupcake: Orange, Orange, 5
Cupcake: Red Velvet, Red, 4
Cupcake: Strawberry, Pink, 10
Cupcake: Vanilla, Tan, 11
Hyper-Raid Objectives + Chaos Deck
Complete objectives while running the lesson. Draw a Chaos Deck card whenever output is wrong.
Zone Alpha: Array Wastes
- Demonstrate one task where fixed-size arrays are superior.
- Demonstrate one task where
ArrayListdominates. - Explain one case where
LinkedListhelps and one where it hurts.
Zone Beta: Queue Canyon
- Build a custom queue scenario with at least 6 events.
- Predict exact dequeue order before execution.
- If mismatch, draw Chaos Deck A:
Temporal Jitter: explain FIFO vs random expectation.
Zone Gamma: Generic Forge
- Add a new
Collectablesubtype and instantiate at least 4 objects. - Ensure type safety (no raw collections).
- If type confusion appears, draw Chaos Deck B:
Type Corruption: add explicit generic bounds or typed declarations.
Zone Delta: Comparator Colosseum
- Create ranking logic with primary + secondary sorting criteria.
- Show one tie case and prove tie-break outcome.
- If order looks wrong, draw Chaos Deck C:
Mirror Sort: print pre-sort and post-sort states and explain changes.
Sudden Death Rule
At the end, justify every collection choice by operation pattern:
- insertion behavior
- lookup behavior
- traversal behavior
- ordering behavior
Prestige Ranks
Chaos Bronze(2 zones)Chaos Diamond(4 zones)Chaos Mythic(all zones + correct final raid + complexity reflection)
Hack Helpers
Below is a starter Queue and a Linked List implementation. This implements Generic type and implements Iterable to support Java ForEach (enhanced For) loops.
In my experience, building your own Data Structures can help you to understand fundamentals of a Computer Language. To use a Data Structure you will need data. The developer working with LL, Stacks, and Queues needs to can learn how to manage different Data Types, this helps you learn about the Java Data Type Object as a generic form of an instance of a class and the Generic type <T> as generic for of a Data Type within a class definition.
/**
* Implementation of a Double Linked List; forward and backward links point to adjacent Nodes.
*
*/
public class LinkedList<T>
{
private T data;
private LinkedList<T> prevNode, nextNode;
/**
* Constructs a new element
*
* @param data, data of object
* @param node, previous node
*/
public LinkedList(T data, LinkedList<T> node)
{
this.setData(data);
this.setPrevNode(node);
this.setNextNode(null);
}
/**
* Clone an object,
*
* @param node object to clone
*/
public LinkedList(LinkedList<T> node)
{
this.setData(node.data);
this.setPrevNode(node.prevNode);
this.setNextNode(node.nextNode);
}
/**
* Setter for T data in DoubleLinkedNode object
*
* @param data, update data of object
*/
public void setData(T data)
{
this.data = data;
}
/**
* Returns T data for this element
*
* @return data associated with object
*/
public T getData()
{
return this.data;
}
/**
* Setter for prevNode in DoubleLinkedNode object
*
* @param node, prevNode to current Object
*/
public void setPrevNode(LinkedList<T> node)
{
this.prevNode = node;
}
/**
* Setter for nextNode in DoubleLinkedNode object
*
* @param node, nextNode to current Object
*/
public void setNextNode(LinkedList<T> node)
{
this.nextNode = node;
}
/**
* Returns reference to previous object in list
*
* @return the previous object in the list
*/
public LinkedList<T> getPrevious()
{
return this.prevNode;
}
/**
* Returns reference to next object in list
*
* @return the next object in the list
*/
public LinkedList<T> getNext()
{
return this.nextNode;
}
}
import java.util.Iterator;
/**
* Queue Iterator
*
* 1. "has a" current reference in Queue
* 2. supports iterable required methods for next that returns a generic T Object
*/
class QueueIterator<T> implements Iterator<T> {
LinkedList<T> current; // current element in iteration
// QueueIterator is pointed to the head of the list for iteration
public QueueIterator(LinkedList<T> head) {
current = head;
}
// hasNext informs if next element exists
public boolean hasNext() {
return current != null;
}
// next returns data object and advances to next position in queue
public T next() {
T data = current.getData();
current = current.getNext();
return data;
}
}
/**
* Queue: custom implementation
* @author John Mortensen
*
* 1. Uses custom LinkedList of Generic type T
* 2. Implements Iterable
* 3. "has a" LinkedList for head and tail
*/
public class Queue<T> implements Iterable<T> {
private String name = null; // name of queue
private int count = 0; // number of objects in queue
LinkedList<T> head = null, tail = null;
/** Constructor
* Queue constructor
* Parameters to name queue and Data Objects
*/
public Queue(String name, T[]... seriesOfObjects) {
this.name = name;
this.addList(seriesOfObjects);
}
/** Queue Accessors / Getters
* These gettrs return Queue Meta Data
*/
public String getName() {return this.name;}
public int getCount() {return this.count;}
/** Add an object
* Parameter is a Data Object that is added to end of the Queue,
*
* @param data, is the data to be inserted in the Queue.
*/
public void add(T data) {
// add new object to end of Queue
LinkedList<T> tail = new LinkedList<>(data, null);
if (this.head == null) // initial condition
this.head = this.tail = tail;
else { // nodes in queue
this.tail.setNextNode(tail); // current tail points to new tail
this.tail = tail; // update tail
}
this.count++;
}
/** Add a list of Objects
* Paramter is a serise of Data Objects to be added to Queue
*
*/
public void addList(T[]... seriesOfObjects) { //accepts multiple generic T lists
for (T[] objects: seriesOfObjects)
for (T data : objects) {
this.add(data);
}
}
/** Delete Head Element
* Returns the data of head.
*
* @return data, the dequeued data
*/
public T delete() {
T data = this.peek();
if (this.tail != null) { // initial or empty condition
this.head = this.head.getNext(); // current tail points to new tail
if (this.head != null) {
this.head.setPrevNode(tail);
}
this.count--;
}
return data;
}
/** Peak at Head Data
* Returns the data of head element
*
* @return this.head.getData(), the head data in Queue.
*/
public T peek() {
return this.head.getData();
}
/** Get Head
* Returns the head object
*
* @return this.head, the head object in Queue.
*/
public LinkedList<T> getHead() {
return this.head;
}
/** Get Tail
* Returns the tail object
*
* @return this.tail, the last object in Queue
*/
public LinkedList<T> getTail() {
return this.tail;
}
/** Implements Iterator
* Returns the iterator object.
*
* @return this, instance of object
*/
public Iterator<T> iterator() {
return new QueueIterator<>(this.head);
}
/** Print Queue
* Prints which by usage validates iterable and getters
*
*/
public void print() {
System.out.print(this.getName() + " " + this.getCount() +": ");
for (Object obj: this)
System.out.print("" + obj + " ");
System.out.println();
}
}
/**
* Driver Class
* Tests queue with string, integers, and mixes of Classes and types
*/
class QueueTester {
public static void main(String[] args)
{
// Create iterable Queue of Words
String[] words = new String[] { "seven", "slimy", "snakes", "sallying", "slowly", "slithered", "southward"};
Queue qWords = new Queue("Words", words);
qWords.print();
// Create iterable Queue of Integers
Integer[] numbers = new Integer[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Queue qNums = new Queue("Integers", numbers );
qNums.print();
// Create iterable Queue of NCS Collectable
Animal.setOrder(Animal.KeyType.name);
Alphabet.setOrder(Alphabet.KeyType.letter);
Cupcake.setOrder(Cupcake.KeyType.flavor);
// Illustrates use of a series of repeating arguments
Queue qCollect = new Queue("Collectable",
Alphabet.alphabetData(),
Animal.animals(),
Cupcake.cupcakes()
);
qCollect.print();
// Create iterable Queue of Mixed types of data
Queue qMix = new Queue("Mixed");
qMix.add("Start");
qMix.addList(
words,
numbers,
Alphabet.alphabetData(),
Animal.animals(),
Cupcake.cupcakes()
);
qMix.add("End");
qMix.print();
}
}
QueueTester.main(null);
Words 7: seven slimy snakes sallying slowly slithered southward
Integers 10: 0 1 2 3 4 5 6 7 8 9
Collectable 41: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Lion Pig Robin Cat Kitty Dog Red Velvet Orange Lemon Apple Blueberry Blackberry Strawberry Vanilla Chocolate
Mixed 60: Start seven slimy snakes sallying slowly slithered southward 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Lion Pig Robin Cat Kitty Dog Red Velvet Orange Lemon Apple Blueberry Blackberry Strawberry Vanilla Chocolate End
FINAL EVENT: NullPointer Titan (Mythic Roguelike)
Run the simulation below, then mutate rules and survive longer.
Mandatory Systems
- Wave Queue (
Queue): FIFO enemy events and crisis cards. - Roster (
ArrayList): heroes mutate stats over time. - Loot Ledger (
HashMap): track resources and phase rewards. - Defeat Log (
Stack): LIFO replay of destroyed enemies.
Titan Phases
- Phase I: Scan - moderate damage, introduces status effects.
- Phase II: Corrupt - queue mutates with surprise events.
- Phase III: Overflow - high burst damage, tie-break leaderboard required.
Win Conditions
- Survive at least 8 rounds.
- Trigger at least 2 random chaos events.
- Print final leaderboard sorted by power then name.
- Print loot + defeat stack replay.
God Mode Extensions (optional)
- Add revive mechanic that consumes loot.
- Add class abilities (
tank,burst,support). - Add anti-stall rule preventing infinite survival loops.
import java.util.*;
class Raider implements Comparable<Raider> {
String name;
String role;
int hp;
int power;
boolean shielded;
Raider(String name, String role, int hp, int power) {
this.name = name;
this.role = role;
this.hp = hp;
this.power = power;
this.shielded = false;
}
boolean alive() {
return hp > 0;
}
@Override
public int compareTo(Raider other) {
int byPower = Integer.compare(other.power, this.power); // descending power
return (byPower != 0) ? byPower : this.name.compareTo(other.name); // tie-break by name
}
@Override
public String toString() {
return name + "{" + role + ", HP=" + hp + ", PWR=" + power + (shielded ? ", SHIELD" : "") + "}";
}
}
class NullPointerTitanRaid {
static String drawChaos(Random rng) {
String[] events = {
"Null Storm", "Overclock Surge", "Memory Leak", "Cache Blessing", "Concurrency Rift"
};
return events[rng.nextInt(events.length)];
}
public static void main(String[] args) {
Random rng = new Random(1337); // deterministic replay
// ArrayList: dynamic roster
ArrayList<Raider> party = new ArrayList<>(Arrays.asList(
new Raider("Queue Knight", "tank", 165, 20),
new Raider("Generic Witch", "burst", 110, 31),
new Raider("Array Berserker", "bruiser", 145, 24),
new Raider("Map Ranger", "support", 120, 26),
new Raider("Stack Monk", "support", 105, 23)
));
// Queue: waves and injected chaos events stay FIFO
Queue<String> waves = new LinkedList<>(Arrays.asList(
"Bug Swarm", "Null Phantom", "Heap Ogre", "Race Condition",
"Leak Serpent", "Kernel Dragon", "Titan Core", "Overflow Hydra"
));
// HashMap: loot/resources and score ledger
HashMap<String, Integer> loot = new HashMap<>();
loot.put("gold", 0);
loot.put("gems", 0);
loot.put("patches", 0);
// Stack: track defeated events in reverse chronological order
Stack<String> defeatLog = new Stack<>();
int titanHP = 520;
int round = 1;
int phase = 1;
int chaosCount = 0;
System.out.println("=== NULLPOINTER TITAN RAID: MYTHIC MODE ===");
while (!waves.isEmpty() && titanHP > 0 && party.stream().anyMatch(Raider::alive)) {
String enemy = waves.remove();
Raider actor = party.get((round - 1) % party.size());
if (!actor.alive()) {
round++;
continue;
}
if (titanHP <= 340 && phase == 1) {
phase = 2;
System.out.println("\n>> TITAN PHASE II: CORRUPT PROTOCOL <<");
waves.add("Corrupted Iterator");
waves.add("Ghost of Null");
} else if (titanHP <= 170 && phase == 2) {
phase = 3;
System.out.println("\n>> TITAN PHASE III: OVERFLOW APOCALYPSE <<");
waves.add("Segfault Revenant");
}
String chaos = drawChaos(rng);
chaosCount++;
int base = actor.power + rng.nextInt(11);
int bonus = 0;
int incoming = 14 + rng.nextInt(17) + (phase - 1) * 5;
if ("Overclock Surge".equals(chaos)) {
bonus += 12;
} else if ("Cache Blessing".equals(chaos)) {
actor.shielded = true;
bonus += 4;
} else if ("Memory Leak".equals(chaos)) {
actor.power = Math.max(10, actor.power - 2);
} else if ("Null Storm".equals(chaos)) {
bonus -= 5;
} else if ("Concurrency Rift".equals(chaos)) {
// Keep FIFO integrity: we append a destabilizer to the tail only
waves.add("Rift Echo");
}
int dealt = Math.max(0, base + bonus);
titanHP = Math.max(0, titanHP - dealt);
if (actor.shielded) {
incoming = Math.max(0, incoming - 10);
actor.shielded = false;
}
actor.hp = Math.max(0, actor.hp - incoming);
// Role effects
if ("support".equals(actor.role) && actor.alive()) {
loot.put("patches", loot.get("patches") + 2);
}
if ("burst".equals(actor.role) && rng.nextInt(100) < 20) {
titanHP = Math.max(0, titanHP - 10); // burst proc
}
loot.put("gold", loot.get("gold") + 18 + rng.nextInt(18));
loot.put("gems", loot.get("gems") + (phase >= 2 ? 2 : 1));
if (actor.alive()) loot.put("patches", loot.get("patches") + 1);
defeatLog.push(enemy + " [phase " + phase + "]");
System.out.println("Round " + round + " | " + actor.name + " vs " + enemy);
System.out.println(" chaos=" + chaos + ", dealt=" + dealt + ", incoming=" + incoming);
System.out.println(" titanHP=" + titanHP + ", actor=" + actor);
// Emergency revive uses loot patches
if (!actor.alive() && loot.get("patches") >= 5) {
loot.put("patches", loot.get("patches") - 5);
actor.hp = 35;
System.out.println(" >> REVIVE PROTOCOL: " + actor.name + " returns with 35 HP");
}
round++;
if (round > 18) break; // anti-infinite safety rule
}
Collections.sort(party);
System.out.println("\n=== APOCALYPSE RESULTS ===");
System.out.println("Titan defeated: " + (titanHP == 0));
System.out.println("Chaos events triggered: " + chaosCount);
System.out.println("Final leaderboard: " + party);
System.out.println("Loot ledger: " + loot);
System.out.println("Defeat log replay (latest first):");
while (!defeatLog.isEmpty()) {
System.out.println(" - " + defeatLog.pop());
}
System.out.println("\nExplain why ArrayList, Queue, HashMap, and Stack each matched their task.");
}
}
NullPointerTitanRaid.main(null);