← Java tutorials

LESSON 25 / 40 · FREE TUTORIAL

A Mob List That Grows

Use ArrayList to edit dynamic waves

90–110 minutesThis chapter uses JDK 21 and does not require the preview function.Aggregation and procedure practice

WHAT YOU WILL MAKE

Make dynamic wave editor DynamicWave

The wave editor can add monsters, change monsters, and delete monsters at any time. A growing list gives you freedom, but also requires you to recheck the index. You'll use testing to prove that failed actions didn't change the team. Prerequisites: arrays, loops, methods and objects. The array is like a fixed hotbar, and the ArrayList is like an expanding warehouse, but the administrator number still starts from 0.

Before you start: It is recommended to complete Lessons 15 and 21–24 first

Both containers store elements of the same type in order
  1. ArrayThe length is fixed after creation
  2. ArrayListAutomatic management of capacity
  3. size()Current number of elements

01 / 16

Before starting: predict first, then let the program answer

This chapter uses JDK 21 and does not require the preview function. The story is borrowed from the block world, and the example runs on the ordinary Java console; if the values ​​​​and rules are course models, they will be clearly stated.

Each common example is a complete independent file, saved according to the file name marked on the box. Place each example in a different folder to avoid duplicate names of auxiliary classes. When the complete project is divided into sections, they are connected sequentially in the same file.

Terminal command: replace the Chinese placeholder with the actual file name and class name

javac -encoding UTF-8 ExampleFile.java
java ExampleClass
  • Prediction: Write the expected output first, or draw out the changes in references and data.
  • Verification: Check line by line after running, and then only change one condition.
  • Explanation: Explain whether the results come from Java rules, course contracts, or input data.

02 / 16

The array is fixed but the list will expand

The number of monster waves changes constantly while editing. Arrays require a predetermined length, and ArrayList can be added or removed as needed.

Both containers store elements of the same type in order
  1. ArrayThe length is fixed after creation
  2. ArrayListAutomatic management of capacity
  3. size()Current number of elements
Acceptance comparison table
CompareArrayArrayList
LengthFixed after creationCan change dynamically
Readitems[i]list.get(i)
Modificationitems[i] = xlist.set(i, x)
AddNeed to create another arraylist.add(x)
Length querylengthsize()

03 / 16

Generic restriction element type

Generics block errors at the compilation stage. ArrayList<String> means that the list elements must be Strings.

Angle brackets indicate which items the queue only accepts
  1. List<String>Element types are checked at compile time
  2. ArrayList<>Create variable implementation
  3. GenericCannot add incompatible values

L25Example02.java · Complete independent example

import java.util.*;
public class L25Example02 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        wave.add("Zombie");
        System.out.println(wave);
    }
}

Output

[Zombie]

BadGeneric.java · Intentional error example

import java.util.*;
public class BadGeneric {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        wave.add(20);
    }
}

04 / 16

add get and size

add puts the element at the end, and size returns the current number of elements. When the list has 3 elements, the legal indexes are 0, 1, and 2.

Add to the end Read with index Use size to query the quantity
  1. add(x)Add at the end
  2. add(i,x)Insert and move subsequent items
  3. get(i)Read legal index

L25Example03.java · Complete independent example

import java.util.*;
public class L25Example03 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        wave.add("Zombie"); wave.add("Skeleton");
        wave.add(1, "Creeper");
        System.out.println(wave);
        System.out.println(wave.size());
        System.out.println(wave.get(2));
    }
}

Output

[Zombie, Creeper, Skeleton]
3
Skeleton

05 / 16

set and remove by index

set replaces the specified element, and the list size remains unchanged; remove(index) deletes the element, and the size is reduced by 1.

Replacement does not change the size. Deletion will move the following elements to the left.
  1. set(i,x)Replacement, size remains unchanged
  2. remove(i)Delete, reduce size by 1
  3. Right elementIndex moves left

L25Example04.java · Complete independent example

import java.util.*;
public class L25Example04 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>(List.of("Zombie", "Skeleton"));
        System.out.println(wave.set(1, "Spider"));
        System.out.println(wave.remove(0));
        System.out.println(wave);
    }
}

Output

Skeleton
Zombie
[Spider]

06 / 16

Delete index or delete object

remove(1) usually means deleting position No. 1, and remove("Spider") means deleting the first matching object. Lists of integers require special care.

remove overload selects version according to parameter type
  1. remove(int)Parameter is index
  2. remove(Object)Delete first matching value
  3. Integer.valueOfExplicitly select object for deletion

L25Example05.java · Complete independent example

import java.util.*;
public class L25Example05 {
    public static void main(String[] args) {
        List<Integer> ids = new ArrayList<>(List.of(10, 20, 30));
        System.out.println(ids.remove(1));
        System.out.println(ids.remove(Integer.valueOf(10)));
        System.out.println(ids);
    }
}

Output

20
true
[30]

07 / 16

Enhance for reading queue

When you only want to read elements sequentially, enhanced for is most suitable. Direct add or remove during traversal may trigger ConcurrentModificationException.

Read-only traversal is concise and clear
  1. Enhance forSuitable for sequential reading
  2. Structural modificationWill make iteration invalid
  3. Controlled deletionUse removeIf and other solutions

L25Example06.java · Complete independent example

import java.util.*;
public class L25Example06 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>(List.of("Zombie", "Spider"));
        for (String mob : wave) System.out.println(mob);
    }
}

Output

Zombie
Spider

BadIteration.java · Intentional error example

import java.util.*;
public class BadIteration {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>(List.of("A", "B", "C"));
        for (String mob : wave) wave.remove(mob);
    }
}

08 / 16

Index loop supports controlled modification

Ordinary for is suitable when replacing a specified position. If you delete multiple elements in a forward direction, do not skip the new elements that have just been moved after deletion.

Use ordinary for when position is needed
  1. Reverse order cycleStarting from the last index
  2. Delete current itemDo not change smaller index
  3. No skip itemsAdjacent matching can also be deleted

L25Example07.java · Complete independent example

import java.util.*;
public class L25Example07 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>(List.of("Zombie", "Zombie", "Spider"));
        for (int i = wave.size() - 1; i >= 0; i--)
            if ("Zombie".equals(wave.get(i))) wave.remove(i);
        System.out.println(wave);
    }
}

Output

[Spider]

09 / 16

removeIf security filtering

When conditionally removing all matches, removeIf is more straightforward than a handwritten index loop.

Give the deletion conditions to the list for unified execution
  1. removeIfDelete matches conditionally
  2. constant.equalsSafe handling null
  3. Variable listNecessary prerequisite for deletion

L25Example08.java · Complete independent example

import java.util.*;
public class L25Example08 {
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        wave.add(null); wave.add("Zombie"); wave.add("Spider");
        wave.removeIf("Zombie"::equals);
        System.out.println(wave);
    }
}

Output

[null, Spider]

10 / 16

Empty list and index boundary

An empty list can exist normally, but get(0) will throw IndexOutOfBoundsException.

When size is 0, there is no element No. 0
  1. Empty listCan exist but cannot get(0)
  2. Legal index0 <= i < size()
  3. size()Not the last index

L25Example09.java · Complete independent example

import java.util.*;
public class L25Example09 {
    static String safeGet(List<String> wave, int i) {
        return wave != null && i >= 0 && i < wave.size() ? wave.get(i) : null;
    }
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        System.out.println(safeGet(wave, 0));
        wave.add("Spider");
        System.out.println(safeGet(wave, 0));
        System.out.println(safeGet(wave, 1));
    }
}

Output

null
Spider
null
Acceptance comparison table
sizeLegal indexAnnotations
0Noneget(0) out of bounds
10Only the first item
30 to 2Finally, size minus 1

11 / 16

methods preferentially receive List contracts

If the method only requires the common operations of List, the parameter type can be written as List<String>, and the caller can still pass in ArrayList.

The caller can choose different list implementations
  1. Parameter ListRely on universal contract
  2. Object ArrayListProvide variable implementation
  3. Return falseIllegal requests are not modified

L25Example10.java · Complete independent example

import java.util.*;
public class L25Example10 {
    static boolean addMob(List<String> wave, String mob) {
        if (wave == null || mob == null || mob.isBlank()) return false;
        wave.add(mob.strip());
        return true;
    }
    static boolean replace(List<String> wave, int index, String mob) {
        if (wave == null || index < 0 || index >= wave.size()
                || mob == null || mob.isBlank()) return false;
        wave.set(index, mob.strip());
        return true;
    }
    static int removeAll(List<String> wave, String mob) {
        if (wave == null || mob == null || mob.isBlank()) return 0;
        String target = mob.strip();
        int before = wave.size();
        wave.removeIf(target::equals);
        return before - wave.size();
    }
    static void show(List<String> wave) {
        if (wave == null) { System.out.println("No queue"); return; }
        for (int i = 0; i < wave.size(); i++)
            System.out.println(i + ": " + wave.get(i));
    }
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        System.out.println(addMob(wave, " Spider "));
        System.out.println(replace(wave, 1, "Zombie"));
        System.out.println(wave);
    }
}

Output

true
false
[Spider]

12 / 16

Three types of test dynamic queues

Dynamic containers change frequently, and testing must simultaneously check the return value, size, and element order.

Add, replace, delete all verification status
  1. Previous snapshotCopy element sequence
  2. Perform operationsOnly test one item at a time
  3. Post comparisonReturn value, size, order
Acceptance comparison table
SceneOperationExpected
NormalAdd Zombiesize increased by 1
NormalReplace index 1Size remains unchanged but elements change
BorderDelete the last oneSuccessful and in correct order
ErrorIndex equals sizeReturn false and the data remains unchanged
ErrorAdd empty nameReturn false and the data remains unchanged

13 / 16

Challenge Blueprint Dynamic Wave Editor

The challenge program is called DynamicWave. It receives List<String> parameter, and the main program uses ArrayList to save the waves.

Add, replace, delete and display the composition editing process
  1. addMobOnly responsible for adding
  2. replaceOnly responsible for replacement
  3. removeAllDelete all by name

Acceptance sequence

  • Add Zombie, Skeleton, Zombie in sequence.
  • Replace element No. 1 with Spider, and the size is still 3.
  • Delete all Zombies, 2 should be deleted.
  • Finally, position No. 0 is displayed as Spider.
  • Illegal indexes and empty names return false, and the content and order of the list remain unchanged.

14 / 16

Challenges Realizing Dynamic Waves

Create a new DynamicWave.java, compile and run using Java 21.

Generic lists are safely edited via methods
  1. Add three itemsZombie、Skeleton、Zombie
  2. Replace the middleChange to Spider
  3. Filter zombiesOne Spider left

DynamicWave.java · Complete stand-alone example

import java.util.*;
public class DynamicWave {
static boolean addMob(List<String> wave, String mob) {
    if (wave == null || mob == null || mob.isBlank()) return false;
    wave.add(mob.strip());
    return true;
}
static boolean replace(List<String> wave, int index, String mob) {
    if (wave == null || index < 0 || index >= wave.size()
            || mob == null || mob.isBlank()) return false;
    wave.set(index, mob.strip());
    return true;
}
static int removeAll(List<String> wave, String mob) {
    if (wave == null || mob == null || mob.isBlank()) return 0;
    String target = mob.strip();
    int before = wave.size();
    wave.removeIf(target::equals);
    return before - wave.size();
}
static void show(List<String> wave) {
    if (wave == null) { System.out.println("No queue"); return; }
    for (int i = 0; i < wave.size(); i++)
        System.out.println(i + ": " + wave.get(i));
}
    public static void main(String[] args) {
        List<String> wave = new ArrayList<>();
        addMob(wave, "Zombie"); addMob(wave, "Skeleton");
        addMob(wave, "Zombie");
        System.out.println("Replacement=" + replace(wave, 1, "Spider"));
        System.out.println("Delete=" + removeAll(wave, "Zombie"));
        System.out.println("Reject empty names=" + !addMob(wave, " "));
        show(wave);
    }
}

Output

Replacement=true
Delete=2
Reject empty names=true
0: Spider

15 / 16

Expansion tasks and unit review

Lessons 21 to 25 complete inheritance, polymorphism, interfaces, value objects, and dynamic collections. The job submits code, test sheets and a fault record at the same time.

The third-level task continues to expand the data warehouse
  1. Type designInheritance, interface, value object
  2. Container operationsGenerics, indexes, additions and deletions
  3. Delivery evidenceCode and test records

Third gear operation

  • Wooden Pickaxe: Use ArrayList to manage task lists, supporting addition, completion and display.
  • Iron Pickaxe: Write removeAllByName to delete all creatures with the same name and return the number deleted.
  • Diamond Pickaxe: Use Collections.shuffle to randomly shuffle waves and keep a copy of the original order for comparison.
Acceptance comparison table
Self-evaluationCanStill need to practice
I would use a generic list□□
I can safely add and delete elements□□
I would test list boundaries□□

16 / 16

Own acceptance record

Beautiful output is the starting point, and being able to explain boundaries is considered mastery. Select one case each from normal, borderline and error, write down your expectations before running, and leave evidence after running.

Leave a basis for judgment
CategoryThe operation you selectedCheck key points
NormalA common input or a successful actionIs the result consistent line by line?
BorderRange endpoints and adjacent valuesIs there one more step or one less thing?
ErrorIllegal quantity, missing object or bad dataWhether it is explicitly rejected and whether the status is maintained
  • Bug records: triggering conditions, actual phenomena, causes, and minimum repairs.
  • Illustrated record: describe the starting point, end point and meaning of each arrow.
  • Adaptation record: only change one rule, and modify expectations and tests at the same time.

TAKE IT WITH YOU

Take the lesson offline

The complete lesson, code, diagrams and exercises, in your preferred language.