
Java programátor expert
Prioritné fronty prinášajú poriadok tam, kde nestačí obyčajné FIFO – od real‑time simulácií až po plánovanie úloh. V tejto časti seriálu o dátových štruktúrach ti, ako môžeš efektívne použiť triedu PriorityQueue z balíka java.until. Prevedieme ťa konštruktormi, metódami aj úskaliami synchronizácie a chýbať nebude ani názorný príklad z nemocničnej praxe.
V článku sa dozvieš:
Zatiaľ čo priority queue označuje abstraktný dátový typ, ktorý spracúva prvky podľa priority, PriorityQueue je konkrétna trieda v Jave, ktorú použiješ na jeho implementáciu. Dátová štruktúra PriorityQueue, ktorá umožňuje efektívne spracovanie prvkov vo fronte podľa ich priority, resp. nami definovaného poradia.
PriorityQueue kolekcia – hierarchia dedičnosti (zdroj)
Java PriorityQueue je jednou z množstva dátových štruktúr na jednoduchšiu prácu s dátami. V našom seriáli si postupne predstavíme aj rôzne ďalšie dátové štruktúry. Ukážeme si, ako s nimi pracovať, aké operácie sa s nimi dajú robiť, aké sú najčastejšie používané metódy. Spomenieme výhody a nevýhody a kedy je vhodné konkrétnu dátovú štruktúru použiť.
Dátová kolekcia PriorityQueue (k dispozícii od Java 1.5) je implementáciou rozhrania Queue, ktorá predstavuje abstraktný dátový typ podobný fronte. Rozdiel spočíva v algoritme odstraňovania prvkov.
Zatiaľ čo klasická fronta pracuje na princípe FIFO (First In, First out) a prvky sa pridávajú na koniec fronty a odstraňujú na začiatku, v PriorityQueue sú prvky uložené podľa ako už názov dátovej štruktúry napovedá podľa priority. Najskôr sa vždy odstráni prvok s najvyššou prioritou, pred ostatnými s nižšou prioritou. Ak existuje viacero prvkov s najvyššou prioritou, jeden z týchto prvkov sa ľubovoľne vyberie ako najmenší prvok.
Interná štruktúra je binárna halda (binary heap), čo zabezpečuje, že prístup k prvku s najvyšším prioritou (metóda peek() alebo poll()) trvá O(1), zatiaľ čo vkladanie a odstraňovanie v čase O(log n). Podľa potreby môžeme použiť prirodzené poradie prvkov (ak implementujú Comparable), alebo vlastný Comparator, ktorý definuje prioritu.
Nulové prvky nie sú povolené, rovnako sa neumožňuje vkladanie prvkov neporovnateľných prvkov, takých, kde sa nedá určiť priorita.
Kolekcia PriorityQueue nie je synchronizovaná. To znamená, že nie bezpečná pre viacvláknové spracovanie. Na prácu s viacerými vláknami poskytuje Java triedu PriorityBlockingQueue, ktorá implementuje rozhranie BlockingQueue.
Pre vytvorenie inštancie PriorityQueue máme k dispozícii niekoľko konštruktorov:
PriorityQueue()
PriorityQueue(int initialCapacity)
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
PriorityQueue(Collection<? extends E> c)
PriorityQueue(Comparator<? super E> comparator)
PriorityQueue(PriorityQueue<? extends E> c)
PriorityQueue(SortedSet<? extends E> c)
Medzi základné operácie, ktoré môžeme vykonávať s PriorityQueue, patria:
Kompletný prehľad metód triedy PriorityQueue, nájdeš v oficiálnej dokumentácii
PriorityQueue je ideálny v prípadoch, keď:
Teraz si ukážeme, ako prakticky pracovať s kolekciou PriorityQueue v Java. Dátová štruktúra PriorityQueue sa vynikajúco hodí pre simulovanie udalostí, resp. programy, ktoré vyžadujú spracovanie prvkov podľa priority.
Dnes si naprogramujeme realistický simulátor nemocnice. Do nemocnice prichádzajú pacienti (alebo sú privezení sanitkou). Každý pacient sa najskôr v nemocnici rýchlo vyšetrí a určí sa závažnosť jeho stavu. Stav pacientov sa ohodnotí od 1 až 10, kde 10 je život ohrozujúci stav a 1, kde pacient je takmer zdravý.
Pacientov po vyšetrení umiestnime do PriorityQueue (prioritná fronta na liečbu), kde budú čakať, kým sa dostanú na rad. Lekári vždy začnú liečiť pacienta s najvyššou závažnosťou, takéhoto pacienta označíme na konci liečby ako vyliečeného. Aby sme simulátor nemocnice spravili trochu realistický, tak pacientom so stavom 5 až 10 sa v každom ťahu (liečba iného pacienta) zhorší stav o +1. Ak mal pacient kriticky stav 10 a nedostal sa prioritne na vyšetrenie, zomiera. Priebeh simulácie vypíšeme prehľadne na konzolu.
Patient.java
import java.util.Random;
public class Patient {
private final String name;
private int severity; // Severity from 1 (lowest) to 10 (highest)
public Patient(String name) {
this.name = name;
this.severity = examine();
}
// Simulate examination by assigning random severity between 1 and 10
private int examine() {
Random random = new Random();
return random.nextInt(10) + 1;
}
public String getName() {
return name;
}
public int getSeverity() {
return severity;
}
public void increaseSeverity() {
severity++;
}
@Override
public String toString() {
return name + " (severity: " + severity + ")";
}
}
HospitalSimulation.java
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
public class HospitalSimulation {
private final PriorityQueue<Patient> triageQueue;
public HospitalSimulation() {
// Create a PriorityQueue sorting patients from highest severity to lowest
this.triageQueue = new PriorityQueue<>(Comparator.comparingInt(Patient::getSeverity).reversed());
}
// Add patients to the queue
public void admitPatients(Patient[] patients) {
System.out.println("🚑 Admitting patients...");
for (Patient patient : patients) {
triageQueue.add(patient);
System.out.println("✅ Admitted: " + patient);
}
}
// Treat patients based on their severity
public void treatPatients() {
System.out.println("\n🏥 Starting treatment...");
while (!triageQueue.isEmpty()) {
Patient currentPatient = triageQueue.poll(); // Remove patient with the highest severity
System.out.println("👨⚕️ Treating: " + currentPatient);
simulateTreatment();
System.out.println("✅ " + currentPatient.getName() + " has been treated.\n");
// After treating a patient, increase the severity of waiting patients
worsenWaitingPatients();
}
System.out.println("🎉 All patients have been treated or deceased!");
}
// Increase severity for waiting patients, handle deaths
private void worsenWaitingPatients() {
if (triageQueue.isEmpty()) {
return;
}
List<Patient> updatedPatients = new ArrayList<>();
List<Patient> deceasedPatients = new ArrayList<>();
while (!triageQueue.isEmpty()) {
Patient patient = triageQueue.poll();
if (patient.getSeverity() >= 5) {
patient.increaseSeverity();
if (patient.getSeverity() > 10) {
deceasedPatients.add(patient); // Patient dies
continue;
}
}
updatedPatients.add(patient);
}
// Rebuild the queue with updated patients
triageQueue.addAll(updatedPatients);
// Report deaths
for (Patient deceased : deceasedPatients) {
System.out.println("⚰️ Patient " + deceased.getName() + " has died while waiting (severity exceeded 10).");
}
}
// Simulate the time taken to treat a patient
private void simulateTreatment() {
try {
Thread.sleep(500); // Simulate short delay (500ms)
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Entry point
public static void main(String[] args) {
// Create hospital simulation instance
HospitalSimulation hospital = new HospitalSimulation();
// Create 10 patients arriving at the hospital
Patient[] patients = {
new Patient("Alena"),
new Patient("Beáta"),
new Patient("Ctibor"),
new Patient("Daniel"),
new Patient("Elena"),
new Patient("František"),
new Patient("Gregor"),
new Patient("Helena"),
new Patient("Igor"),
new Patient("Jana")
};
// Simulate admitting and treating patients
hospital.admitPatients(patients);
hospital.treatPatients();
}
}
Výstup z tohto príkladu môže vyzerať nasledovne:
Pripravili sme pre teba súbory so spomínaným príkladom vo forme kódu, ktorý si môžeš spustiť priamo v Jave. Stiahni si Java kód pre PriorityQueue tu.
Súvisiace články