Skip to lab content

WEEK 13 · OBJECT ORIENTED PROGRAMMING LAB

Quiz leaderboard with chained comparators

EXPERIMENT 26Java 11+Console program

THE SYLLABUS TASK

What you need to build

Create a quiz scoring leaderboard.
Full syllabus requirements: classes, fields and methods
Create a quiz scoring leaderboard. Class: Player with attributes name, score, timeTaken Data Structure: ArrayList<Player> players Task: Sort players using Comparator-Highest score first and if tie → lesser timeTaken comes first Methods • displayTop() - Display Top-5 performers • search() - Search by player name & return their score if found else return “No such Player”. • delete() – Delete a player by name & return “SUCCESS” if found else return “No such Player”.
Input format & conventions

n (0–50); each player has name, score and time in separate lines. Then commands: 1 top five; 2 name search; 3 name delete; 0 exit. Names are unique case-insensitively. Equal score/time ties use name for deterministic output.

UNDERSTAND THE IDEA

Explanation

The comparator ranks score descending, then time ascending. An additional name tie-break makes fully tied records reproducible. displayTop sorts the ArrayList and prints at most five entries.

Search scans names case-insensitively and returns the matching score. Delete uses an Iterator’s remove method rather than modifying the list inside an enhanced for loop.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Read unique players with validated score/time.
  2. Sort by reversed score, increasing time, then name.
  3. Print the first min(5,n) records.
  4. Search by normalized name equality.
  5. Delete safely through an iterator and report the outcome.

SEE THE CORE OPERATION

Flowchart

This chart focuses on the named operation or core OOP decision. Diamonds show conditions; labeled branches show outcomes. Read it with the complete algorithm and source for input handling and other operations. Scroll within the diagram on a phone.

Leaderboard rank comparison

Leaderboard rank comparison

Open full-size flowchart ↗

Java 11+

Complete Java source

Main.java

Download .java
import java.util.*;

public class Main {
    private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);
    static String text() {
        if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
        String value = IN.nextLine().trim();
        if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
        return value;
    }
    static int integer(int low, int high) {
        int value = Integer.parseInt(text());
        if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
        return value;
    }
    static double number(double low, double high) {
        double value = Double.parseDouble(text());
        if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
        return value;
    }
    static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }
static class Player {
        final String name;final int score;final double timeTaken;
        Player(String name,int score,double time){this.name=name;this.score=score;timeTaken=time;}
    }
    static final Comparator<Player> ORDER=Comparator.comparingInt((Player p)->p.score).reversed().thenComparingDouble(p->p.timeTaken).thenComparing(p->p.name);
    static class Leaderboard {
        final ArrayList<Player> players=new ArrayList<>();
        void displayTop(){players.sort(ORDER);System.out.println("Top players:");for(int i=0;i<Math.min(5,players.size());i++){Player p=players.get(i);System.out.printf(Locale.ROOT,"%s | %d | %.2f seconds%n",p.name,p.score,p.timeTaken);}}
        String search(String name){for(Player p:players)if(p.name.equalsIgnoreCase(name))return Integer.toString(p.score);return "No such Player";}
        String delete(String name){Iterator<Player> it=players.iterator();while(it.hasNext())if(it.next().name.equalsIgnoreCase(name)){it.remove();return "SUCCESS";}return "No such Player";}
    }
    // n; name/score/time for each. Then 1=top five, 2 name=search, 3 name=delete, 0=exit.
    public static void main(String[] args){
        try{
            int n=integer(0,50);Leaderboard board=new Leaderboard();
            for(int i=0;i<n;i++){String name=text();int score=integer(0,10000);double time=number(0,1000000);if(!board.search(name).equals("No such Player"))throw new IllegalArgumentException("Duplicate player name.");board.players.add(new Player(name,score,time));}
            while(true){int command=integer(0,3);if(command==0)break;if(command==1)board.displayTop();else if(command==2)System.out.println(board.search(text()));else System.out.println(board.delete(text()));}
        }catch(IllegalArgumentException e){System.out.println("Invalid input: "+e.getMessage());}
    }
}

Copy a source file or download the project to preserve its file names.

USE THE RIGHT ENVIRONMENT

How to compile and run

Install a JDK (Java 11 or newer; JDK 17 is suitable), extract this project’s ZIP into its own folder, then open a terminal in that folder.

javac -encoding UTF-8 Main.java
java Main

The standalone source is named Main.java and its entry class is Main. You may also paste it into an online Java compiler and supply the sample input there. Choose Java explicitly; these pages do not change the site compiler or import code automatically.

Each experiment has its own folder because many use the public class Main. Compile and run one experiment at a time.

UNDERSTAND THE JAVA CODE

Line-by-line explanation

These steps follow the exact Java source above. Related statements are grouped; line numbers count blank lines and restart for each file. Start with main to follow execution, then open the constructors, methods or event handlers it uses.

Start with main →

Main.java

Lines 1–4Import utilities and create the shared input reader
import java.util.*;

public class Main {
    private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);

java.util.* imports utility type names such as Scanner, Locale and collections. Main is the public class matching Main.java. The private static final IN field holds one shared Scanner for System.in; final prevents replacing that reference. The scanner uses Locale.ROOT.

Lines 5–10Read one nonempty trimmed input line
    static String text() {
        if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
        String value = IN.nextLine().trim();
        if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
        return value;
    }

text checks whether a line exists, reads it and removes leading/trailing whitespace. Reject empty text and lengths above 200 by throwing IllegalArgumentException. Otherwise return the string. One field is read per line, so these helpers do not mix nextInt with leftover newlines.

Lines 11–15Parse and range-check an integer field
    static int integer(int low, int high) {
        int value = Integer.parseInt(text());
        if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
        return value;
    }

Integer.parseInt converts the returned string to int or throws NumberFormatException, a subtype of IllegalArgumentException. Reject values outside the caller’s inclusive low/high limits, then return the accepted integer.

Lines 16–20Parse and range-check a finite decimal field
    static double number(double low, double high) {
        double value = Double.parseDouble(text());
        if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
        return value;
    }

Double.parseDouble reads the string as a double. Double.isFinite rejects NaN and infinities as well as the explicit low/high range check. Return only a finite accepted value; exceptions transfer control to a matching catch block.

Line 21Format a labelled numeric result consistently
    static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }

money is a formatting helper: %s prints the label, %.2f prints two decimal places, and %n ends the line. Locale.ROOT fixes locale-sensitive formatting. The helper can also display non-currency quantities; it does not change the stored value.

Lines 22–25Represent one player result
static class Player {
        final String name;final int score;final double timeTaken;
        Player(String name,int score,double time){this.name=name;this.score=score;timeTaken=time;}
    }

Each Player stores a name, score and time taken in final fields. The constructor assigns the accepted input values.

Line 26Define the ranking rules in their correct sequence
    static final Comparator<Player> ORDER=Comparator.comparingInt((Player p)->p.score).reversed().thenComparingDouble(p->p.timeTaken).thenComparing(p->p.name);

Compare score descending, then completion time ascending, then name in String natural order. The second and third rules are used only when earlier comparisons tie.

Lines 27–29Sort results and display at most five players
    static class Leaderboard {
        final ArrayList<Player> players=new ArrayList<>();
        void displayTop(){players.sort(ORDER);System.out.println("Top players:");for(int i=0;i<Math.min(5,players.size());i++){Player p=players.get(i);System.out.printf(Locale.ROOT,"%s | %d | %.2f seconds%n",p.name,p.score,p.timeTaken);}}

Leaderboard owns an ArrayList. displayTop sorts that list using ORDER, then visits only min(5,size) entries. It therefore also works with fewer than five or no players.

Line 30Search for a player name without case sensitivity
        String search(String name){for(Player p:players)if(p.name.equalsIgnoreCase(name))return Integer.toString(p.score);return "No such Player";}

Scan players and use equalsIgnoreCase for identity matching. Return the found score as text, or the explicit absent-player message.

Lines 31–32Delete through the iterator that is doing the scan
        String delete(String name){Iterator<Player> it=players.iterator();while(it.hasNext())if(it.next().name.equalsIgnoreCase(name)){it.remove();return "SUCCESS";}return "No such Player";}
    }

Use Iterator.remove immediately after next identifies a matching player. This safely removes the current entry during traversal. Return SUCCESS after one removal, or the absent message after the full scan.

Lines 33–37Read unique player records before processing commands
    // n; name/score/time for each. Then 1=top five, 2 name=search, 3 name=delete, 0=exit.
    public static void main(String[] args){
        try{
            int n=integer(0,50);Leaderboard board=new Leaderboard();
            for(int i=0;i<n;i++){String name=text();int score=integer(0,10000);double time=number(0,1000000);if(!board.search(name).equals("No such Player"))throw new IllegalArgumentException("Duplicate player name.");board.players.add(new Player(name,score,time));}

Read up to 50 players with checked scores and finite times. The existing search rule prevents duplicate names even with different letter case. Add only accepted new Player objects.

Lines 38–41Dispatch top-five, search and delete operations
            while(true){int command=integer(0,3);if(command==0)break;if(command==1)board.displayTop();else if(command==2)System.out.println(board.search(text()));else System.out.println(board.delete(text()));}
        }catch(IllegalArgumentException e){System.out.println("Invalid input: "+e.getMessage());}
    }
}

Command 0 ends the session. Other commands call the corresponding Leaderboard method; search and delete read an additional name line. The catch reports malformed or out-of-range input.

FOLLOW THE STATE

Dry run

  1. Players A=(10,20s), B=(10,10s), C=(5,1s).
  2. B ranks first because its score ties A but its time is lower.
  3. Searching B returns 10.
  4. Deleting B returns SUCCESS; searching again returns No such Player.

CHECK THE BEHAVIOUR

Sample input & output

These outputs were captured by running the exact delivered Java project.

Sample 1

INPUT
3
A
10
20
B
10
10
C
5
1
1
2
B
3
B
2
B
0
OUTPUT
Top players:
B | 10 | 10.00 seconds
A | 10 | 20.00 seconds
C | 5 | 1.00 seconds
10
SUCCESS
No such Player

Sample 2

INPUT
0
1
2
Unknown
3
Unknown
0
OUTPUT
Top players:
No such Player
No such Player

Common mistakes

  • Reverse only the score comparator, not the whole chained comparator.
  • Use Iterator.remove for safe traversal deletion.
  • Print at most the actual player count.

WHY THIS GROWTH RATE?

Time and space complexity

Top display sort O(n log n) comparisons; search/delete O(nL) with name length L; O(n) stored players.

Sorting performs O(n log n) comparator work in the worst case; numeric comparisons are fixed-cost, while the final name tie-break can examine text.

Displaying top five writes at most five records. Search scans names; deletion also may shift the remaining ArrayList entries, keeping a linear worst-case bound.

The sorting implementation may use O(n) temporary storage in addition to the player list.

These bounds describe the implemented data processing. Input/output text length, scheduling, framework rendering and runtime/library internals are identified separately where they matter. Educational limits bound the actual examples.

PREPARE FOR YOUR LAB VIVA

Related viva questions & answers

6 focused questions

Try answering aloud, then expand to check your reasoning. Questions focus on this program’s classes, state and behavior.

Why is reversed applied before thenComparingDouble?

It reverses the score ordering only. Reversing the whole comparator afterward would also make longer time rank ahead, contradicting the task.

What determines the winner when scores tie?

The lesser timeTaken comes first. If both score and time tie, this implementation uses name as a documented deterministic extra tie-break.

Why is min(5, players.size()) used for displayTop?

The list may contain fewer than five players. That bound prevents reading nonexistent positions while still showing up to five performers.

Why does delete use an Iterator?

Its remove operation is designed for removing the current element during iteration. Direct list removal inside an enhanced for loop can invalidate the iteration.

Does searching a player require sorting first?

No. The search method scans by name, independently of leaderboard order. Sorting controls rank presentation rather than name lookup correctness.

How does the program prevent ambiguous case-insensitive searches?

It rejects registrations whose names already match case-insensitively. Each accepted search name can therefore identify at most one player.