Programming lesson
Building a Pokemon Collection Manager in Java: A Step-by-Step Guide to Implementing Collection Classes
Learn how to implement a custom collection class in Java using arrays, with a Pokemon-themed example that covers Thing class, Collection class, and Driver class as per ICS 141 assignment requirements.
Introduction: Why Custom Collection Classes Matter in Java
In Java, the ability to create your own collection classes is a fundamental skill that bridges the gap between understanding basic arrays and mastering the Java Collections Framework. While ArrayList and HashMap are powerful, implementing a collection class from scratch teaches you memory management, encapsulation, and algorithm design. This tutorial uses a Pokemon theme to demonstrate how to build a PokemonCollection class, following the structure of the ICS 141 assignment. Whether you're a student tackling a similar assignment or a developer brushing up on OOP, this guide will walk you through every step.
Step 1: Designing the Thing Class (Pokemon)
Your Thing class must have three instance variables: one int for aggregate calculations, one String for categorization, and a third variable of either type. For our Pokemon example:
- int combatPower (used for total/max calculations)
- String type (categorization: Fire, Water, Grass, etc.)
- String name (the Pokemon's name)
All variables are private, with getters and setters. The toString() method returns a tab-separated line, and equals() uses case-insensitive string comparison. Here's the implementation:
public class Pokemon {
private int combatPower;
private String type;
private String name;
public Pokemon(int combatPower, String type, String name) {
this.combatPower = combatPower;
this.type = type;
this.name = name;
}
// Getters and setters
public int getCombatPower() { return combatPower; }
public void setCombatPower(int combatPower) { this.combatPower = combatPower; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return name + "\t" + type + "\t" + combatPower;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Pokemon)) return false;
Pokemon other = (Pokemon) obj;
return this.combatPower == other.combatPower &&
this.type.equalsIgnoreCase(other.type) &&
this.name.equalsIgnoreCase(other.name);
}
}Notice how equalsIgnoreCase ensures that "charizard", "Charizard", and "CHARIZARD" are treated as equal. This is crucial for the contains and countOccurrences methods later.
Step 2: Building the Collection Class (PokemonCollection)
The collection class must use a plain Java array (no ArrayList). It holds a name (String), numItems (int), and items (Pokemon[]). The constructor takes the collection name and maximum capacity. Let's implement it step by step.
Instance Variables and Constructor
public class PokemonCollection {
private String name;
private int numItems;
private Pokemon[] items;
public PokemonCollection(String name, int maxSize) {
this.name = name;
this.numItems = 0;
this.items = new Pokemon[maxSize];
}Add Methods
We need two add methods: one that takes a Pokemon object, and an overloaded version that takes three parameters (combatPower, type, name) and creates a new Pokemon internally.
public void add(Pokemon p) {
if (numItems < items.length) {
items[numItems] = p;
numItems++;
} else {
System.out.println("Collection is full!");
}
}
public void add(int combatPower, String type, String name) {
add(new Pokemon(combatPower, type, name));
}Size, Total, Greatest, CountCategory, Contains, CountOccurrences
These methods demonstrate the power of the int attribute and string categorization. For example, total() sums all combat powers, greatest() finds the Pokemon with highest combat power, and countCategory("Fire") returns how many Fire-type Pokemon exist (case-insensitive).
public int size() {
return numItems;
}
public int total() {
int sum = 0;
for (int i = 0; i < numItems; i++) {
sum += items[i].getCombatPower();
}
return sum;
}
public Pokemon greatest() {
if (numItems == 0) return null;
Pokemon max = items[0];
for (int i = 1; i < numItems; i++) {
if (items[i].getCombatPower() > max.getCombatPower()) {
max = items[i];
}
}
return max;
}
public int countCategory(String category) {
int count = 0;
for (int i = 0; i < numItems; i++) {
if (items[i].getType().equalsIgnoreCase(category)) {
count++;
}
}
return count;
}
public boolean contains(Pokemon p) {
for (int i = 0; i < numItems; i++) {
if (items[i].equals(p)) {
return true;
}
}
return false;
}
public int countOccurrences(Pokemon p) {
int count = 0;
for (int i = 0; i < numItems; i++) {
if (items[i].equals(p)) {
count++;
}
}
return count;
}toString – Tabular Format
The toString() method must display the collection in a table. We'll use simple string concatenation with tabs.
@Override
public String toString() {
String result = "Pokemon Collection: " + name + "\n";
result += "Name\tType\tCombatPower\n";
result += "------------------------\n";
for (int i = 0; i < numItems; i++) {
result += items[i].toString() + "\n";
}
return result;
}
}Step 3: Creating the Driver Class
The driver class tests all methods. It creates a collection with capacity 10, adds 5 Pokemon using both add methods, then calls each method in order (size, total, greatest, countCategory, contains, countOccurrences, toString). Hard-coded values are used as per assignment guidelines.
public class PokemonDriver {
public static void main(String[] args) {
PokemonCollection myCollection = new PokemonCollection("My Pokemon", 10);
// Add 5 items using both add methods
myCollection.add(new Pokemon(1500, "Fire", "Charizard"));
myCollection.add(new Pokemon(1200, "Water", "Blastoise"));
myCollection.add(new Pokemon(1800, "Dragon", "Dragonite"));
myCollection.add(900, "Grass", "Venusaur");
myCollection.add(1100, "Electric", "Pikachu");
// Call methods in order
System.out.println("Size: " + myCollection.size());
System.out.println("Total Combat Power: " + myCollection.total());
System.out.println("Greatest Pokemon: " + myCollection.greatest());
System.out.println("Count of Fire type: " + myCollection.countCategory("Fire"));
System.out.println("Contains Charizard? " + myCollection.contains(new Pokemon(1500, "Fire", "Charizard")));
System.out.println("Occurrences of Pikachu: " + myCollection.countOccurrences(new Pokemon(1100, "Electric", "Pikachu")));
System.out.println(myCollection.toString());
}
}Real-World Application: Pokemon GO and Data Aggregation
As of May 2026, Pokemon GO remains a popular mobile game with millions of active players. Imagine you're building a tool to manage your Pokemon storage. The total() method could compute your total combat power for gym battles, greatest() identifies your strongest Pokemon, and countCategory("Dragon") helps you decide which type to focus on for raids. This assignment mirrors real-world data management tasks used in apps like Pokemon Home or even inventory systems for e-commerce.
Tips for Success
- Use meaningful variable names: Instead of
a,b, usecombatPower,type,name. - Add comments: At the top of each file, include your name, date (May 23, 2026), and a brief description.
- Indent consistently: Use your IDE's auto-format feature to align curly braces.
- Test edge cases: What if the collection is full? What if you search for a Pokemon that doesn't exist? Handle these gracefully.
- Follow method signatures exactly: The assignment specifies parameter types and return types; stick to them.
Conclusion
By completing this assignment, you'll have a solid grasp of creating custom collection classes in Java using arrays. This knowledge is directly applicable to more advanced topics like implementing your own linked lists or trees. Remember to zip your Part 1 files as DillonArraysOfObjectCollectionPart1 (replace with your name) before moving to Part 2. Good luck!