← Java tutorials

LESSON 26 / 40 · FREE TUTORIAL

Scoreboards and HashMap

Check scores by name, update scoreboard safely

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

WHAT YOU WILL MAKE

Make player scoreboard ScoreBoard

There is a new player in the server, and there is no need to add another variable to the scoreboard. Use names as keys to read out scores, and then use clear rules to handle missing points, deductions, sorting, and integer upper limits. Prerequisites: variables, methods, loops, ArrayList and generics. You don't have to create a player100Score when you let the 100th player join today.

Before you start: It is recommended to complete Lesson 25 first

Player name is connected to own score
  1. keyPlayer name, unique search identifier
  2. valueScore, repeatable
  3. entryA key-value mapping

01 / 14

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 / 14

keys and values form a lookup table

Map is like a server scoreboard. The player name is the key and the score is the value; the program quickly finds the corresponding score through the name.

Only one copy of the current value is retained for the same key. Two different players can have exactly the same score.

Player name is connected to own score
  1. keyPlayer name, unique search identifier
  2. valueScore, repeatable
  3. entryA key-value mapping
Acceptance comparison table
ConceptScoreboard ExampleRules
key keyPlayer name AlexUnique in the same Map
value valueScore 12Duplication allowed
Mapping entryAlex corresponds to 12A set of keys and values

03 / 14

Create HashMap and put data

Write the key and value types when declaring. String, Integer in angle brackets means that only string keys and integer values ​​can be used.

put adds or replaces get key query
  1. Map<String,Integer>Clear key value type
  2. putThe key with the same name replaces the old value
  3. sizeCurrent record number

L26Example02.java · Complete independent example

import java.util.*;
public class L26Example02 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alex", 12); scores.put("Steve", 12);
        scores.put("Alex", 20);
        System.out.println(scores.get("Alex"));
        System.out.println(scores.size());
    }
}

Output

20
2

BadMapType.java · Intentional error example

import java.util.*;
public class BadMapType {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alex", "12");
    }
}

04 / 14

Safety cumulative score

A put with the same name will replace the old value without increasing the number of records. The get of the new key returns null, and direct unboxing and addition will fail. Let's look at simple counting first, and then establish clear contracts for large numbers, null and illegal old records.

Both getOrDefault and merge can handle new players
  1. does not existgetOrDefault gives default 0
  2. null already existsThe default value does not replace it
  3. Safe additionConvert large numbers to long first and check

L26Example03.java · Complete independent example

import java.util.*;
public class L26Example03 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alex", scores.getOrDefault("Alex", 0) + 5);
        scores.merge("Alex", 3, Integer::sum);
        System.out.println(scores.get("Alex"));
    }
}

Output

8

05 / 14

Non-existing keys must be handled explicitly

get returns null for a non-existent key. The scoreboard can choose to display 0, or it can explicitly tell the user that the player cannot be found.

containsKey distinguishes between no players and a score of 0
  1. getReturn null for missing key
  2. containsKeyDetermine whether the key exists
  3. 0 point playerPresence and absence are different

L26Example04.java · Complete independent example

import java.util.*;
public class L26Example04 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alex", 0);
        System.out.println(scores.get("Mia"));
        System.out.println(scores.getOrDefault("Mia", 0));
        System.out.println(scores.containsKey("Alex"));
        System.out.println(scores.containsKey("Mia"));
    }
}

Output

null
0
true
false

06 / 14

entrySet gets the key and value at once

Just traversing keySet and then calling get can also work, but entrySet directly provides complete records, which makes the expression clearer.

Traverse each scoring record
  1. entrySetGet the key and value at the same time
  2. HashMapNo commitment to traversal order
  3. TreeMap copyKey generation stable display

L26Example05.java · Complete independent example

import java.util.*;
public class L26Example05 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>(Map.of("Steve", 8, "Alex", 12));
        for (var entry : new TreeMap<>(scores).entrySet())
            System.out.println(entry.getKey() + "=" + entry.getValue());
    }
}

Output

Alex=12
Steve=8

07 / 14

Delete players and understand the order

remove(key) deletes the entire record and returns the old value; returns null when the key does not exist. clear() will clear the entire table.

remove returns the old value HashMap without queuing
  1. remove(key)Delete and return the old value
  2. clear()Clear all records
  3. Display orderNeed to clearly choose to implement

L26Example06.java · Complete independent example

import java.util.*;
public class L26Example06 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>(Map.of("Alex", 12));
        System.out.println(scores.remove("Alex"));
        System.out.println(scores.remove("Alex"));
        scores.clear();
        System.out.println(scores.size());
    }
}

Output

12
null
0

08 / 14

Method parameters are oriented to Map contract

If the method only requires the general operation of Map, the parameters should be written as Map<K,V> instead of being locked as HashMap.

The caller can choose different Map implementations
  1. Name checkReject empty names, remove leading and trailing blanks
  2. long calculationPrevent int addition from overflowing
  3. Last updatedData remains unchanged when rejected

L26Example07.java · Complete independent example

import java.util.*;
public class L26Example07 {
    static boolean addScore(Map<String, Integer> scores,
                            String player, int delta) {
        if (scores == null || player == null || player.isBlank()) return false;
        String key = player.strip();
        Integer old = scores.get(key);
        if (scores.containsKey(key) && (old == null || old < 0)) return false;
        int current = old == null ? 0 : old;
        long next = (long) current + delta;
        if (next > Integer.MAX_VALUE) return false;
        scores.put(key, (int) Math.max(0L, next));
        return true;
    }
    static int getScore(Map<String, Integer> scores, String player) {
        if (scores == null || player == null || player.isBlank()) return 0;
        String key = player.strip();
        Integer value = scores.get(key);
        if (!scores.containsKey(key)) return 0;
        if (value == null || value < 0)
            throw new IllegalStateException("Scoring record corrupted: " + key);
        return value;
    }
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        System.out.println(addScore(scores, " Alex ", 5));
        System.out.println(addScore(scores, "Alex", -20));
        System.out.println(getScore(scores, "Alex"));
        scores.put("Alex", Integer.MAX_VALUE);
        System.out.println(addScore(scores, "Alex", 1));
        System.out.println(getScore(scores, "Alex"));
    }
}

Output

true
true
0
false
2147483647

09 / 14

Ranking list is copied first and then sorted

HashMap is suitable for checking scores by name, but is not suitable for expressing the first and second places. When making a ranking list, copy the records to the list and then sort them.

Map is responsible for searching and List is responsible for orderly display
  1. Map.entryCopy unmodifiable value entries
  2. In descending order of scoresDetermine ranking
  3. Same score by nameOutput is repeatable

L26Example08.java · Complete independent example

import java.util.*;
public class L26Example08 {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>(Map.of("Steve", 8, "Alex", 8));
        List<Map.Entry<String, Integer>> ranking = new ArrayList<>();
        scores.forEach((name, score) -> ranking.add(Map.entry(name, score)));
        ranking.sort(Map.Entry.<String, Integer>comparingByValue().reversed()
            .thenComparing(Map.Entry.comparingByKey()));
        scores.put("Alex", 99);
        for (var entry : ranking)
            System.out.println(entry.getKey() + "=" + entry.getValue());
    }
}

Output

Alex=8
Steve=8

10 / 14

Three types of tests keep scoreboard

When testing the extra points method, don't just look at one line of output. Also check if the failed operation secretly changed the Map.

Normal boundary and wrong input must leave evidence
  1. Normal bonus pointsOld value increased
  2. Minimum 0 pointsPoint deduction clamping
  3. Failed CheckEqual to snapshot before operation
Acceptance comparison table
SceneOperationExpected
NormalAlex plus 5Score increased by 5
BorderPlayers with 0 points will be deducted 1Still 0
ErrorAdd 5 to empty nameReturn false and the data remains unchanged
Errornull MapReturn false without throwing an exception
QueryNo player existsReturn 0

L26Example09.java · Complete independent example

import java.util.*;
public class L26Example09 {
    static boolean addScore(Map<String, Integer> scores,
                            String player, int delta) {
        if (scores == null || player == null || player.isBlank()) return false;
        String key = player.strip();
        Integer old = scores.get(key);
        if (scores.containsKey(key) && (old == null || old < 0)) return false;
        int current = old == null ? 0 : old;
        long next = (long) current + delta;
        if (next > Integer.MAX_VALUE) return false;
        scores.put(key, (int) Math.max(0L, next));
        return true;
    }
    static int getScore(Map<String, Integer> scores, String player) {
        if (scores == null || player == null || player.isBlank()) return 0;
        String key = player.strip();
        Integer value = scores.get(key);
        if (!scores.containsKey(key)) return 0;
        if (value == null || value < 0)
            throw new IllegalStateException("Scoring record corrupted: " + key);
        return value;
    }
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>(Map.of("Alex", 12));
        Map<String, Integer> before = new HashMap<>(scores);
        boolean ok = addScore(scores, " ", 5);
        System.out.println(!ok && scores.equals(before));
        System.out.println(getScore(scores, "Mia"));
    }
}

Output

true
0

11 / 14

Challenge Blueprint Player Scoreboard

The challenge program is called ScoreBoard. It should support addition and subtraction of points for new players and old players, safe query and deletion, while not relying on the traversal order of HashMap.

Add deduction, query, delete to form a complete process
  1. Add playerStart from 0 when it does not exist
  2. Query missingThis lesson returns 0
  3. Delete playerMapping completely removed

Acceptance sequence

  • Alex adds 5 from 12, and the query results in 17.
  • Steve deducted 20 from 8, and the lowest score was clamped to 0.
  • Querying missing Mia returns 0, but containsKey is identifiable as missing.
  • An empty name operation returns false and the scoreboard remains unchanged.
  • Deleting Steve returns the old score of 0, leaving only 1 player.

12 / 14

Scoreboard Program

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

Map contract encapsulates secure scoring operations
  1. AlexAdd 12 to 5 to get 17
  2. Steve8 buckle 20 to get 0
  3. Delete SteveLast 1 player left

ScoreBoard.java · Complete standalone example

import java.util.*;
public class ScoreBoard {
static boolean addScore(Map<String, Integer> scores,
                        String player, int delta) {
    if (scores == null || player == null || player.isBlank()) return false;
    String key = player.strip();
    Integer old = scores.get(key);
    if (scores.containsKey(key) && (old == null || old < 0)) return false;
    int current = old == null ? 0 : old;
    long next = (long) current + delta;
    if (next > Integer.MAX_VALUE) return false;
    scores.put(key, (int) Math.max(0L, next));
    return true;
}
static int getScore(Map<String, Integer> scores, String player) {
    if (scores == null || player == null || player.isBlank()) return 0;
    String key = player.strip();
    Integer value = scores.get(key);
    if (!scores.containsKey(key)) return 0;
    if (value == null || value < 0)
        throw new IllegalStateException("Scoring record corrupted: " + key);
    return value;
}
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        addScore(scores, "Alex", 12); addScore(scores, "Steve", 8);
        addScore(scores, "Alex", 5); addScore(scores, "Steve", -20);
        System.out.println("Alex=" + getScore(scores, "Alex"));
        System.out.println("Steve=" + getScore(scores, "Steve"));
        System.out.println("Mia=" + getScore(scores, "Mia"));
        System.out.println("Empty name=" + addScore(scores, " ", 5));
        System.out.println("Delete=" + scores.remove("Steve"));
        System.out.println("Number of players=" + scores.size());
    }
}

Output

Alex=17
Steve=0
Mia=0
Empty name=false
Delete=0
Number of players=1

13 / 14

Expansion tasks and clearance review

The job submits code, test table and a fault record at the same time. Explain why a certain field was chosen as a key.

Three-level task expansion server statistics
  1. Select keyExplain the search purpose
  2. Definition failedMissing, empty name, overflow
  3. Verification sequenceCannot rely on HashMap order

Third gear operation

  • Wooden pickaxe: Create an item price list and support new additions, price changes and inquiries.
  • Iron pickaxe: Count the number of occurrences of a group of dropped objects.
  • Diamond Pickaxe: Copy the scoring records to the list and output the top three with the highest scores.
Acceptance comparison table
Self-evaluationCanStill need to practice
I can design keys and values□□
I can safely query for missing keys□□
I don't rely on HashMap order□□

14 / 14

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.