- Leren
- Informatica & ICT
- Programmeren
- Java programmeren: klassen, overerving, collecties en streams
- HashMaps en collecties in Java
HashMaps en collecties in Java
HashMap op sleutel op te slaan en op te zoeken, waarden te tellen en te groeperen en met HashSet unieke waarden te bewaren. Bekijk welke garanties de interfaces List, Set en Map bieden, sorteer lijsten en geef je eigen klassen een natuurlijke volgorde met Comparable. Kies de collectie die bij elke taak past.BeginnerLes 7 van 11 in Cursus Java programmeren: klassen, overerving, collecties en streams
Wat je leert
- Maak hashmaps, voeg sleutel-waardeparen toe, lees, wijzig en verwijder ze, doorloop de inhoud van een map en gebruik je eigen klassen als sleutels die de map kan terugvinden.
- Maak een HashMap en voeg sleutel-waardeparen toe, vraag die op, werk die bij en verwijder dieDeclareer een HashMap met de sleutel- en waardetypen, sla paren op met put, lees ze met get en handel ontbrekende sleutels af.
- Doorloop sleutels, waarden en sleutel-waardeparenDoorloop elk paar in een map met keySet, values en entrySet, weet welke volgorde je kunt verwachten en wijzig een map veilig tijdens het doorlopen.
- Gebruik je eigen objecten als sleutels in een mapMaak objecten van je eigen klassen bruikbaar als sleutels door equals en hashCode samen te overschrijven en laat sleutels ongewijzigd zolang ze in een map staan.
- Maak een HashMap en voeg sleutel-waardeparen toe, vraag die op, werk die bij en verwijder die
- Gebruik maps om te tellen hoe vaak waarden voorkomen, verwante waarden onder één sleutel te verzamelen en opzoektabellen te maken waarmee je objecten snel vindt.
- Tel hoe vaak waarden voorkomen en bereken totalen per sleutelMaak frequentietabellen en lopende totalen met het getOrDefault-patroon en lees de resultaten correct.
- Groepeer waarden onder één sleutel met lijsten en verzamelingen als mapwaardenMaak maps waarvan de waarden lijsten of verzamelingen zijn, voeg elementen toe aan de juiste groep en voorkom fouten waardoor groepen verloren gaan of onbruikbaar worden.
- Zoek objecten en prijzen snel op met een index op basis van een mapGebruik maps als opzoektabellen en indexen die objecten via een unieke sleutel vinden, bewaar ze binnen een klasse en houd meerdere indexen consistent.
- Tel hoe vaak waarden voorkomen en bereken totalen per sleutel
- Gebruik HashSet om waarden zonder duplicaten op te slaan, lidmaatschap snel te testen en verzamelingen te combineren met vereniging, doorsnede en verschil.
- Sla unieke waarden op en test lidmaatschap met HashSetVoeg waarden toe aan een HashSet, controleer en verwijder ze, gebruik het resultaat van add en verwijder duplicaten uit een lijst.
- Combineer verzamelingen en behoud hun volgordeBereken verenigingen, doorsneden en verschillen zonder de oorspronkelijke verzamelingen te wijzigen, vergelijk verzamelingen en kies een geordende verzameling als de volgorde ertoe doet.
- Sla unieke waarden op en test lidmaatschap met HashSet
- Declareer collecties met hun interfacetypen, weet welke garanties elke interface biedt en gebruik de gemeenschappelijke methoden die bij verschillende implementaties werken.
- Declareer variabelen en parameters met interfacetypenLeg uit wat een interfacetype is, declareer collecties als List, Set of Map en schrijf methoden die elke implementatie accepteren.
- Weet welke garanties List, Set en Map biedenVergelijk de garanties van de drie interfaces, herken hun belangrijkste implementaties en werk met niet-wijzigbare collecties die met of zijn gemaakt.
- Declareer variabelen en parameters met interfacetypen
- Sorteer lijsten in natuurlijke volgorde, geef je eigen klassen een natuurlijke volgorde door Comparable te implementeren en gebruik die volgorde in gesorteerde verzamelingen en maps.
- Sorteer lijsten met getallen en stringsSorteer een lijst ter plaatse met Collections.sort, voorspel de volgorde van strings en sorteer in omgekeerde volgorde of zonder onderscheid tussen hoofdletters en kleine letters.
- Implementeer Comparable in je eigen klasseGeef een klasse een natuurlijke volgorde door Comparable te implementeren en compareTo te schrijven met veilige vergelijkingen van getallen en strings.
- Gebruik aanvullende sorteercriteria en werk met gesorteerde verzamelingen en mapsOrden objecten op meerdere velden, houd compareTo consistent met equals en gebruik TreeSet en TreeMap om gegevens gesorteerd te houden.
- Sorteer lijsten met getallen en strings
- Vergelijk hoe snel veelgebruikte bewerkingen in elke collectie zijn en kies het type dat past bij wat het programma met zijn gegevens moet doen.
- Vergelijk de kosten van veelgebruikte collectiebewerkingenSchat hoe de tijd voor opzoeken, toevoegen en verwijderen groeit met de grootte van elke collectie en herken trage patronen.
- Kies een collectie die bij de taak pastBeantwoord enkele vragen over volgorde, duplicaten en opzoeken om te kiezen tussen arrays, lijsten, verzamelingen en maps en combineer ze voor complexere gegevens.
- Vergelijk de kosten van veelgebruikte collectiebewerkingen
HashMaps en collecties in Java: uitleg
1Sla gegevens op en zoek die op via een sleutel met HashMap
Een productcode kan rechtstreeks naar een voorraadhoeveelheid leiden, zonder een lijst te doorzoeken. Maps maken die koppeling expliciet. Om ze goed te gebruiken, moet je ook ontbrekende sleutels afhandelen, paren veilig doorlopen en definiëren wanneer twee sleutelobjecten als gelijk gelden. Begin met een kleine voorraadmap en volg hoe de inhoud verandert en hoe Java een opgeslagen sleutel terugvindt. Dezelfde gelijkheidsregels zijn van belang wanneer je verzamelingen gebruikt.
1.1 Maak een HashMap en voeg sleutel-waardeparen toe, vraag die op, werk die bij en verwijder die
Een map bewaart een koppeling van elke sleutel naar één waarde. In een voorraadmap is een productnaam de sleutel en de hoeveelheid de waarde. Twee producten kunnen dezelfde hoeveelheid hebben, maar één productsleutel kan niet twee afzonderlijke sleutel-waardeparen hebben.
Maak de map
import java.util.HashMap;
HashMap<String, Integer> stock = new HashMap<>();
Het eerste type, String, beschrijft sleutels. Het tweede, Integer, beschrijft waarden. Met de diamantnotatie <> rechts kan Java die typen afleiden. Typeargumenten moeten referentietypen zijn: gebruik Integer, Double, Character of Boolean in plaats van int, double, char of boolean. Java zet een gewone int via autoboxing om als die als Integer wordt opgeslagen en zet een Integer via unboxing om als een int nodig is.
Volg elke wijziging
stock.put("apple", 12);
stock.put("pear", 7);
Integer previous = stock.put("apple", 15);
De eerste aanroep maakt apple=12; de tweede voegt pear=7 toe. De derde vindt de bestaande sleutel "apple" en vervangt de waarde ervan. De map heeft nu twee sleutel-waardeparen en previous bevat 12. Een put voor een sleutel die nog niet aan een waarde is gekoppeld, geeft null terug.
stock.get("apple") geeft nu 15 terug. stock.remove("pear") geeft 7 terug en verwijdert dat paar. Als je "pear" opnieuw verwijdert, krijg je null terug. size() telt de paren, isEmpty() controleert of er geen paren zijn en clear() verwijdert alle paren.
Handel een ontbrekende sleutel af
stock.get("kiwi") geeft null terug en stock.containsKey("kiwi") geeft false terug. Deze instructie compileert, maar mislukt tijdens de uitvoering:
int quantity = stock.get("kiwi");
Java probeert null via unboxing om te zetten naar een int, waardoor een NullPointerException ontstaat. Gebruik bij een map met hoeveelheden die niet null zijn een standaardwaarde:
int quantity = stock.getOrDefault("kiwi", 0);
Dit geeft 0 zonder "kiwi" aan de map toe te voegen. Je kunt ook containsKey controleren voordat je leest. containsValue(15) vraagt of een van de paren die waarde heeft; de methode kan alle waarden doorzoeken en verschilt dus van het zoeken naar een bekende sleutel.
Een HashMap staat ook een null-sleutel en null-waarden toe. Als een sleutel expliciet aan null is gekoppeld, geeft getOrDefault die opgeslagen null terug, niet de standaardwaarde. Met containsKey onderscheid je dat geval van een ontbrekende sleutel. Voorraadmaps zijn eenvoudiger als elke opgeslagen hoeveelheid een echt getal is.
Vergelijk sleutels en waarden correct
Strings als sleutel zijn hoofdlettergevoelig: "apple", "Apple" en "apple " zijn verschillend. Pas vóór het opslaan en zoeken dezelfde opschoning toe, zoals name.trim().toLowerCase(). Alleen een zoeksleutel opschonen kan inconsistent opgeslagen sleutels niet herstellen.
Vergelijk waarden in wrapperobjecten op inhoud met equals. Als twee paren elk 1000 bevatten, kunnen hun Integer-referenties verschillen, zodat == onwaar kan zijn hoewel hun numerieke inhoud overeenkomt. Nadat je hebt gecontroleerd dat beide waarden niet null zijn, vergelijkt a.equals(b) hun inhoud.
Belangrijkste punten
- Elke unieke sleutel is aan één waarde gekoppeld; waarden mogen vaker voorkomen.
putvervangt een bestaande waarde en geeft de vorige waarde terug.- Een
getvoor een ontbrekende sleutel geeftnullterug; unboxing daarvan veroorzaakt eenNullPointerException. getOrDefaultgeeft alleen een standaardwaarde voor een ontbrekende sleutel.- Vergelijk strings als sleutel consequent en vergelijk waarden in wrapperobjecten op inhoud.
1.2 Doorloop sleutels, waarden en sleutel-waardeparen
Een map kan één zoekactie beantwoorden of al zijn gegevens aan een lus aanbieden. Kies de weergave die de onderdelen geeft die de lus nodig heeft. De weergaven zijn gekoppeld aan de map, dus wijzigingen via die weergaven kunnen ook de map wijzigen.
Kies sleutels, waarden of sleutel-waardeparen
Gebruik keySet() om sleutels te doorlopen. Met een zoekactie binnen die lus kun je elke waarde ophalen:
for (String name : phoneBook.keySet()) {
System.out.println(name + ": " + phoneBook.get(name));
}
Gebruik values() als de sleutels niet relevant zijn. Voor de voorraadhoeveelheden appel 15, peer 7 en vijg 3:
int total = 0;
for (int quantity : stock.values()) {
total += quantity;
}
Het totaal wordt 25. Herhaalde waarden leveren nog steeds afzonderlijke bijdragen: een waardenweergave is geen verzameling.
Een sleutel-waardepaar bevat een sleutel en de bijbehorende waarde samen. Importeer java.util.Map en gebruik Map.Entry<K, V> met getKey() en getValue(). Zo voorkom je een tweede zoekactie:
String best = null;
int bestScore = Integer.MIN_VALUE;
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
if (best == null || entry.getValue() > bestScore) {
best = entry.getKey();
bestScore = entry.getValue();
}
}
Voor Ana 82, Ben 91 en Cai 77 is het resultaat Ben met 91. De test best == null handelt ook een eerste score af die gelijk is aan de kleinst mogelijke int. Bij een lege map blijft best gelijk aan null.
Kies de volgorde expliciet
Een HashMap garandeert geen iteratievolgorde. Je kunt niet uitgaan van invoegvolgorde, gesorteerde volgorde of de volgorde waarin de map vandaag wordt weergegeven. Een LinkedHashMap behoudt de invoegvolgorde; een TreeMap doorloopt gesorteerde sleutels. Als twee scores gelijk zijn, bewaart de lus hierboven de eerste die wordt aangetroffen. Bij een HashMap kan elk van de namen met de hoogste score winnen. Voeg een expliciete regel voor een gelijke stand toe als het resultaat voorspelbaar moet zijn.
Verwijder paren via de iterator
Als je stock.remove(key) aanroept binnen een for-each-lus over stock.keySet(), verander je de sleutels die de lus doorloopt. Dit kan een ConcurrentModificationException veroorzaken. Detectie is niet gegarandeerd; een uitvoering zonder exceptie maakt dit patroon niet geldig.
Gebruik de eigen verwijdermethode van de iterator:
Iterator<Map.Entry<String, Integer>> it =
stock.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue() == 0) {
it.remove();
}
}
Importeer java.util.Iterator. Voor appel 15, peer 0, vijg 0 en kiwi 4 verwijdert dit peer en vijg. Een andere veilige aanpak is om sleutels in een lijst te bewaren, de lus af te maken en die sleutels daarna te verwijderen.
Een bestaande waarde vervangen voegt geen sleutel toe en verwijdert er geen. Tijdens een lus over sleutel-waardeparen kan entry.setValue(entry.getValue() * 2) de hoeveelheden veilig verdubbelen. Voeg tijdens die lus geen nieuwe sleutels toe via de map.
Onderscheid een weergave van een kopie
stock.keySet().remove("apple") verwijdert het bijbehorende sleutel-waardepaar uit de map. Een aparte lijst die je maakt met new ArrayList<>(stock.keySet()) is een kopie van de huidige sleutels. Het sorteren van die lijst verandert de volgorde van de map niet en het verwijderen van een naam uit de lijst verwijdert de bijbehorende koppeling niet.
Belangrijkste punten
- Gebruik
keySet,valuesofentrySetafhankelijk van wat de lus nodig heeft. HashMapheeft geen gegarandeerde iteratievolgorde; bepaal expliciet wat bij een gelijke stand gebeurt.- Gebruik de
remove-methode van een iterator om tijdens het doorlopen te verwijderen. - Een bestaande waarde vervangen is toegestaan tijdens het doorlopen.
- Mapweergaven zijn gekoppeld aan de map; een gekopieerde lijst met sleutels staat er los van.
1.3 Gebruik je eigen objecten als sleutels in een map
Een product-ID die als String is opgeslagen, heeft al bruikbare gelijkheidsregels. Bij een sleutel van je eigen klasse moet je een keuze maken: stellen twee afzonderlijke objecten met dezelfde gegevens dezelfde sleutel voor?
Hashcodes beperken de zoekactie; gelijkheid bevestigt de overeenkomst
Een HashMap gebruikt hashCode() om een sleutel te helpen lokaliseren en equals() om een overeenkomst te controleren. Gelijke sleutels moeten gelijke hashcodes hebben. Verschillende sleutels kunnen een hashcode delen; gelijkheid onderscheidt ze nog steeds. Standaardwaardeklassen zoals String en Integer volgen dit contract al.
Zonder overschrijvingen erft een klasse van Object gelijkheid op basis van identiteit. Twee afzonderlijk gemaakte punten met coördinaten 2 en 3 zijn verschillende sleutels, ook al komen hun velden overeen:
labels.put(new Point(2, 3), "door");
String label = labels.get(new Point(2, 3));
Bij gelijkheid op basis van identiteit is label gelijk aan null. De zoekactie maakte een tweede object, niet het object dat voor het toevoegen is gebruikt.
Overschrijf het paar samen
Hier bepaalt een punt zijn inhoud via de coördinaten:
import java.util.Objects;
final class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Point)) {
return false;
}
Point point = (Point) other;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
De identiteitscontrole handelt hetzelfde object snel af. De typecontrole wijst null en objecten van een niet-verwant type af. De laatste vergelijking controleert beide coördinaten. Objects.hash(x, y) bouwt de hashcode op uit diezelfde velden. Met deze klasse geeft de zoekactie hierboven "door" terug.
Als je alleen equals overschrijft, blijft de overgeërfde hashcodebepaling bestaan. Die garandeert geen gelijke hashcodes voor afzonderlijke objecten met gelijke velden. Een map kan daardoor een gelijke sleutel missen. Een constante hashcode zou de juistheid behouden, maar het zoeken vertragen. Als je beide overschrijvingen in een editor genereert, voorkom je makkelijker dat de gebruikte velden niet overeenkomen.
Gebruik de exacte methodesignatuur
public boolean equals(Point other) overlaadt de methode; die vervangt equals(Object) niet. Bij het opzoeken in een map wordt equals(Object) gebruikt, dus geeft de nieuwe overladen methode de map geen inhoudelijke gelijkheid. De annotatie @Override maakt van een verkeerde signatuur een compilatiefout in plaats van een verborgen fout bij het opzoeken.
Houd de identiteit van een sleutel stabiel
Wijzig velden die de gelijkheid of hashcode van een sleutel bepalen niet zolang de sleutel is opgeslagen. Als een opgeslagen punt verandert van (2, 3) naar (5, 3), kan de map het onder geen van beide waarden meer vinden, ook al is size() ongewijzigd. Verwijder het punt voordat je het wijzigt en voeg het daarna opnieuw toe. Door sleutelvelden final te maken, zoals hierboven, voorkom je deze fout.
Deze regels bepalen ook het lidmaatschap van een HashSet. Een ArrayList gebruikt equals voor zoekacties op inhoud zoals contains; voor die zoekactie zijn geen hashcodes nodig.
Belangrijkste punten
- Gelijke objecten moeten gelijke hashcodes hebben; ongelijke objecten mogen een hashcode delen.
- Overschrijf
equals(Object)enhashCode()met dezelfde velden. @Overrideontdekt een onbedoeld overladenequals-methode.- Laat velden die gelijkheid en hashcode bepalen ongewijzigd zolang een sleutel is opgeslagen.
2Tel, groepeer en indexeer gegevens met maps
Een map kan bruikbare informatie opbouwen terwijl een programma gegevens leest: een aantal voor elk woord, een lijst voor elke cursus of een object voor elke ID. De belangrijke keuze is wat één waarde voorstelt. Een getal bouwt een resultaat op; een lijst of verzameling brengt een groep bijeen; een objectreferentie vormt een index. Volg elke bijwerking vanaf een lege map, zodat duidelijk is wat er gebeurt bij eerste voorkomens, herhaalde sleutels en gedeelde objecten.
2.1 Tel hoe vaak waarden voorkomen en bereken totalen per sleutel
Een frequentietabel heeft één sleutel voor elk uniek element en een getal dat aangeeft hoe vaak het voorkomt. Het eerste voorkomen begint een telling; elk volgend voorkomen werkt hetzelfde sleutel-waardepaar bij.
Bouw een telling element voor element op
HashMap<String, Integer> counts = new HashMap<>();
for (String word : text.split(" ")) {
int oldCount = counts.getOrDefault(word, 0);
counts.put(word, oldCount + 1);
}
Voor een tekst waarin "the" als eerste woord voorkomt, ontbreekt die sleutel aanvankelijk, dus is het oude aantal 0 en het nieuwe aantal 1. De volgende twee keren dat het woord voorkomt, verandert het aantal in dat paar naar 2 en daarna 3. "cat" eindigt op 2; "saw", "dog", "and" en "ran" eindigen elk op 1.
Er zijn 6 unieke woorden, dus is counts.size() gelijk aan 6. De waarden tellen op tot 9, het aantal voorkomens van woorden. Dit zijn verschillende maten: de grootte van de map is niet de lengte van de oorspronkelijke reeks.
De bijwerking kan op één regel worden geschreven:
counts.put(word, counts.getOrDefault(word, 0) + 1);
Een gelijkwaardige langere vorm controleert containsKey, verhoogt een bestaand aantal en slaat 1 op voor een nieuwe sleutel. Alle aantallen die met dit patroon worden opgeslagen, zijn niet null.
Behoud het oude resultaat
Als je voor elk woord counts.put(word, 1) aanroept, overschrijf je elk aantal steeds opnieuw met 1. Zo leg je vast dat een woord bestaat, maar gaat de frequentie verloren. Als je counts.get(word) + 1 aanroept zonder het eerste voorkomen af te handelen, probeert Java null via unboxing om te zetten en mislukt de uitvoering. De standaardwaarde hoort bij het lezen; het verhoogde aantal moet daarna worden teruggeplaatst.
Bepaal wat als hetzelfde element telt
Gelijkheid van strings is exact. "The" en "the" zijn verschillende sleutels, net als "cat" en "cat,". Als de taak die als hetzelfde woord beschouwt, schoon dan elk token op voordat je het telt. Zet tekst om naar kleine letters en verwijder de leestekens die het programma als scheidingstekens behandelt. Gebruik één consistente regel zodat de resultaten niet afhangen van welke schrijfwijze het eerst voorkwam.
De sleutel kan ook een teken zijn. Met HashMap<Character, Integer> levert een lus over "banana".toCharArray() met hetzelfde patroon van lezen, verhogen en opslaan a=3, n=2 en b=1 op.
Tel bedragen op in plaats van voorkomens
Tel voor categorietotalen het huidige bedrag op in plaats van 1:
HashMap<String, Double> totals = new HashMap<>();
double oldTotal = totals.getOrDefault(category, 0.0);
totals.put(category, oldTotal + amount);
Eten 12,50, vervoer 3,20 en eten 7,25 leveren eten 19,75 en vervoer 3,20 op. De standaardwaarde is 0.0, passend bij het waardetype Double. De map heeft nog steeds één paar per categorie, ongeacht hoeveel bedragen hebben bijgedragen.
Lees de voltooide tabel
Doorloop sleutel-waardeparen om het grootste aantal of totaal te vinden en onthoud daarbij de beste sleutel en waarde. Leg zo nodig een regel voor gelijke standen vast. Voor een alfabetisch overzicht kopieert new TreeMap<>(counts) de koppelingen in gesorteerde sleutelvolgorde. De aantallen blijven ongewijzigd; de sleutels sorteren sorteert de categorieën niet op hun totalen.
Belangrijkste punten
- Een frequentieregistratie in een map bewaart één aantal per unieke sleutel.
- Lees het oude aantal met nul als standaardwaarde, tel er één bij op en sla het resultaat op.
- De mapgrootte telt unieke elementen; de som van de aantallen meet het aantal voorkomens.
- Normaliseer tekst consequent voordat je telt.
- Gebruik hetzelfde bijwerkpatroon om bedragen per sleutel op te tellen.
- Kies de volgorde van het overzicht los van de opgetelde waarden.
2.2 Groepeer waarden onder één sleutel met lijsten en verzamelingen als mapwaarden
Een mapwaarde kan een collectie zijn. Als meerdere studenten bij één cursus horen, is de cursus een sleutel en bevat de bijbehorende lijst alle studenten van die cursus. De regel dat maps unieke sleutels hebben, geldt nog steeds: de lijst is de ene waarde voor die cursus.
Maak een groep voordat je er iets aan toevoegt
HashMap<Character, ArrayList<String>> groups = new HashMap<>();
for (String fruit : fruits) {
char letter = fruit.charAt(0);
if (!groups.containsKey(letter)) {
groups.put(letter, new ArrayList<>());
}
groups.get(letter).add(fruit);
}
Voor “appel”, “banaan”, “avocado”, “kers” en “bes” maakt de eerste vrucht de lijst voor a en voegt “appel” toe. “Banaan” maakt de lijst voor b. “Avocado” vindt de bestaande lijst voor a en voegt zichzelf achteraan toe. Het resultaat is a → [appel, avocado], b → [banaan, bes] en k → [kers]. Elke lijst behoudt zijn eigen invoegvolgorde, ook al ordent de HashMap zijn sleutels niet.
De controle kan korter:
groups.putIfAbsent(letter, new ArrayList<>());
groups.get(letter).add(fruit);
putIfAbsent behoudt een bestaande lijst die niet null is. De daaropvolgende get geeft die lijst terug, klaar voor de toevoeging.
Behoud de lijst die je al hebt opgebouwd
Een onvoorwaardelijke groups.put(letter, new ArrayList<>()) binnen de lus vervangt bij elk voorkomen de oude lijst. Na de volgende add blijft alleen de laatste vrucht per letter over. De tegenovergestelde fout is het aanmaken helemaal overslaan: groups.get(letter).add(fruit) werpt voor een nieuwe sleutel een NullPointerException op omdat er nog geen lijst bestaat.
Begrijp de referentie
get geeft de opgeslagen lijstreferentie terug. Als je daar iets aan toevoegt, verandert diezelfde lijst, dus hoef je die niet terug te plaatsen. Dit verklaart ook een fout met gedeelde referenties:
ArrayList<String> shared = new ArrayList<>();
courses.put("Art", shared);
courses.put("Music", shared);
courses.get("Art").add("Jin");
Beide sleutels leiden nu naar [Jin]. Twee sleutels hebben niet twee lijsten gemaakt. Maak voor elke nieuwe groep een nieuwe lijst als de groepen onafhankelijk moeten zijn.
Kies lijsten of verzamelingen binnen groepen
Een lijst behoudt herhaalde toevoegingen. Een verzameling voorkomt duplicaten binnen die groep. Een HashMap<String, HashSet<String>> kan zo de verschillende cursuslidmaatschappen van elke student verzamelen zonder dezelfde student twee keer in één cursus te tellen. De verzamelingsregel geldt binnen elke waarde; de buitenste map heeft nog steeds één paar per cursus.
Controleer bij het lezen van een groep die mogelijk ontbreekt containsKey of gebruik groups.getOrDefault(letter, new ArrayList<>()). Een ontbrekende sleutel levert dan voor die aanroep een lege lijst op. De standaardlijst wordt niet automatisch ingevoegd; als je er iets aan toevoegt, ontstaat geen opgeslagen groep.
Als je het laatste element van een groep verwijdert, blijft de sleutel aan een lege collectie gekoppeld. Als het programma lege groepen niet langer moet vermelden, controleer dan isEmpty() en verwijder ook die sleutel. Tel groepen met de grootte van de buitenste map en elementen met de groottes van de binnenste collecties.
Belangrijkste punten
- Zorg dat een groep bestaat en voeg daarna iets toe aan de opgeslagen lijst of verzameling.
- Maak voor elke onafhankelijke groep een nieuwe collectie.
getgeeft de opgeslagen referentie terug, dus wijzigingen binnen de collectie vereisen geen extraput.- Lijsten als waarde behouden herhalingen; verzamelingen als waarde bewaren unieke groepsleden.
- Een opgeslagen lege groep heeft nog steeds een sleutel-waardepaar in de map.
2.3 Zoek objecten en prijzen snel op met een index op basis van een map
Een index koppelt een identificerende sleutel aan een object. Een bibliotheek kan een boek via het ISBN vinden zonder elk boek in een lijst te doorzoeken. Een opzoektabel gebruikt dezelfde vorm om een code aan een eenvoudige waarde te koppelen, zoals een productprijs.
Kies een identificerende sleutel
Een HashMap<String, Book> kan een ISBN aan het bijbehorende boek koppelen. Elke zoekactie heeft alleen de sleutel nodig. De naam van een persoon is vaak geen goede unieke sleutel: als je een andere persoon met de naam “Sam Lee” onder dezelfde string opslaat, vervangt die de vorige persoon. Gebruik een unieke ID of gebruik een groepeermap als één naam naar meerdere personen moet leiden.
Bewaar een index binnen de klasse die verantwoordelijk is voor de gegevens:
class Library {
private HashMap<String, Book> byIsbn;
Library() {
byIsbn = new HashMap<>();
}
void addBook(Book book) {
byIsbn.put(book.getIsbn(), book);
}
Book findByIsbn(String isbn) {
return byIsbn.get(isbn);
}
}
Hier biedt Book een methode getIsbn() aan. Aanroepers gebruiken addBook en findByIsbn; ze hoeven niet te weten welke mapimplementatie is opgeslagen. Het privéveld geeft de klasse ook één plek om haar regels voor het toevoegen en vervangen van boeken te onderhouden.
Controleer een ontbrekend resultaat
Als findByIsbn de waarde null teruggeeft, is geen boek gevonden. Test het resultaat voordat je er een methode op aanroept:
Book found = library.findByIsbn("978-0-00-000000-2");
if (found != null) {
found.setOnLoan(true);
}
Als je setOnLoan rechtstreeks op een ontbrekend resultaat aanroept, wordt een NullPointerException opgeworpen. Een methode zoals hasBook(isbn) is een andere manier om aanroepers op een sleutel te laten controleren.
Volg gedeelde objecten door indexen heen
Een map slaat een objectreferentie op, geen nieuwe kopie van dat object. Als hetzelfde boek in een lijst met alle boeken, een ISBN-map en een groepering op auteur staat, wijzigt setOnLoan(true) het ene boek waarnaar alle drie verwijzen. Zo voorkom je dat drie afzonderlijke boekobjecten uiteen gaan lopen.
De collectiestructuren zelf moeten nog steeds worden onderhouden. Als je een boek uit de ISBN-map verwijdert, verwijder je de referentie niet uit de lijst van een auteur. Als een bibliotheek beide indexen bijhoudt, moeten de methoden voor toevoegen en verwijderen beide bijwerken. Door die wijzigingen te centraliseren in methoden zoals addBook en removeBook blijven zoekacties consistent.
Gebruik een tabel in een berekening
Stel dat een prijstabel koffie 2,50, thee 2,00 en muffin 3,25 bevat. Voor twee koffie en één muffin:
double cost = 2 * prices.get("coffee") + prices.get("muffin");
Het subtotaal voor de koffie is 5,00; daar 3,25 bij optellen geeft 8,25. De sleutels identificeren producten en de waarden leveren de bedragen. Voor een ontbrekend product moet je vóór de berekening een expliciete keuze maken: een standaardwaarde nul zou het product ongemerkt als gratis behandelen. Dezelfde opzoektechniek kan dus een andere regel voor ontbrekende sleutels nodig hebben dan een frequentietabel.
Belangrijkste punten
- Een index vindt objecten via een identificerende sleutel.
- Gebruik een unieke sleutel of groepeer meerdere overeenkomende objecten expliciet.
- Indexen delen objectreferenties, maar toevoegingen en verwijderingen moeten elke index bijwerken.
- Houd maps met gegevens privé en bied methoden aan die hun regels handhaven.
3Bewaar unieke waarden in verzamelingen
Soms is de vraag eenvoudigweg of een waarde al eerder is voorgekomen. Verzamelingen passen direct bij die vraag en kunnen ook groepen waarden vergelijken, zoals mensen die twee evenementen bezoeken. Doordat ze geen dubbele elementen bevatten, veranderen zowel de code die je schrijft als de betekenis van de uiteindelijke grootte. Werk eerst met lidmaatschap, combineer daarna verzamelingen en kies of hun waarden in invoegvolgorde of gesorteerde volgorde moeten verschijnen.
3.1 Sla unieke waarden op en test lidmaatschap met HashSet
Een verzameling bevat elke waarde hoogstens één keer. Die past bij vragen zoals “Is deze waarde eerder voorgekomen?” of “Hoeveel verschillende waarden komen voor?” Een HashSet controleert lidmaatschap gemiddeld snel zonder een weergavevolgorde te garanderen.
Voeg waarden toe en controleer ze
import java.util.HashSet;
HashSet<String> visited = new HashSet<>();
boolean first = visited.add("Oslo");
boolean second = visited.add("Lima");
boolean third = visited.add("Oslo");
visited.add("Pune");
De drie opgeslagen booleaanse waarden zijn true, true en false. De tweede “Oslo” komt overeen met een bestaand element en wordt dus niet toegevoegd. De grootte van de verzameling is 3. Een dubbele toevoeging vervangt geen genummerde plek, want een verzameling heeft geen index.
visited.contains("Lima") geeft true terug. visited.remove("Lima") verwijdert de waarde en geeft true terug; als je die verwijdering herhaalt, krijg je false terug. Gebruik een for-each-lus om waarden te doorlopen. Er is geen get(0) voor de eerste waarde van een HashSet.
Verwijder duplicaten uit een lijst
ArrayList<Integer> values = new ArrayList<>();
values.add(3);
values.add(1);
values.add(3);
values.add(2);
values.add(1);
HashSet<Integer> distinct = new HashSet<>(values);
De verzameling heeft drie waarden: {1, 2, 3}. De werkelijke iteratievolgorde is niet gespecificeerd. De oorspronkelijke lijst heeft nog steeds vijf elementen omdat de constructor een aparte collectie maakt.
Gebruik new LinkedHashSet<>(values) om de volgorde van eerste voorkomens te behouden: 3, 1, 2. Gebruik new TreeSet<>(values) om gesorteerde waarden te krijgen: 1, 2, 3. Verzamelingen worden hier tussen accolades geschreven om het lidmaatschap te benadrukken; de standaardmethode toString() van Java gebruikt voor verzamelingen vierkante haken.
Detecteer de eerste herhaling
Het resultaat van add kan de lidmaatschapstest en de toevoeging samen uitvoeren:
HashSet<Integer> seen = new HashSet<>();
for (int number : numbers) {
if (!seen.add(number)) {
System.out.println(number);
break;
}
}
Voor [4, 7, 2, 7, 4] geeft de lus 7 weer. Dat is de eerste waarde die voor de tweede keer wordt aangetroffen, ook al kwam 4 eerder in de reeks voor. Voordat 7 wordt herhaald, bevat de verzameling 4, 7 en 2. De mislukte toevoeging zorgt ervoor dat de lus stopt.
Weet wat een verzameling je niet kan vertellen
contains vóór elke add controleren is overbodig, omdat add duplicaten al afhandelt. Een lijst met een contains-controle kan waarden ook uniek houden, maar elke zoekactie in de lijst kan alle opgeslagen elementen doorlopen. Een HashSet drukt de regel direct uit.
Gelijkheid is van belang voor je eigen objecten. Een HashSet<Point> heeft een equals op basis van inhoud en een bijpassende hashCode nodig om afzonderlijk gemaakte punten met gelijke coördinaten samen te nemen. Zonder die overschrijvingen blijven afzonderlijke instanties afzonderlijke elementen.
Tot slot meet size() het aantal unieke waarden, niet het aantal voorkomens. De negen woorden in “de kat zag de hond en de kat rende” leveren zes unieke woorden op. Een verzameling vertelt je niet meer dat “de” drie keer voorkwam; gebruik een frequentietabel in een map als die aantallen van belang zijn.
Belangrijkste punten
HashSetbewaart unieke waarden zonder index of gegarandeerde volgorde.addgeeft false terug als er al een gelijke waarde aanwezig is.- Een gekopieerde verzameling verwijdert duplicaten zonder de oorspronkelijke lijst te wijzigen.
- Gebruik het resultaat van
addom een herhaalde waarde te detecteren. - De grootte van een verzameling telt unieke waarden; voor frequenties is een map nodig.
3.2 Combineer verzamelingen en behoud hun volgorde
Verzamelingen kunnen groepen beschrijven en hun lidmaatschap vergelijken. Als twee evenementenlijsten dezelfde persoon meerdere keren bevatten, brengt een verzameling elke lijst eerst terug tot de aanwezige personen. Bewerkingen op verzamelingen beantwoorden daarna vragen over minstens één van beide evenementen, beide evenementen of slechts één ervan.
Combineer twee groepen
Laat maandag {Ana, Ben, Cai, Dee} zijn en dinsdag {Ben, Dee, Eva}.
| Vraag | Methode op een kopie van maandag | Resultaat |
|---|---|---|
| Wie was op minstens één van beide dagen aanwezig? | addAll(tuesday) | {Ana, Ben, Cai, Dee, Eva} |
| Wie was op beide dagen aanwezig? | retainAll(tuesday) | {Ben, Dee} |
| Wie was alleen op maandag aanwezig? | removeAll(tuesday) | {Ana, Cai} |
Deze bewerkingen zijn vereniging, doorsnede en verschil. Ze wijzigen de verzameling waarop ze worden aangeroepen en geven een booleaanse waarde terug die aangeeft of die is gewijzigd. Ze geven geen nieuwe resultaatverzameling terug.
Behoud het origineel
Maak een kopie voordat je het lidmaatschap wijzigt als maandag nog nodig is:
HashSet<String> both = new HashSet<>(monday);
both.retainAll(tuesday);
both begint met alle vier de namen van maandag. retainAll verwijdert Ana en Cai omdat ze op dinsdag ontbreken. Maandag zelf blijft ongewijzigd. Om alleen dinsdag te vinden, begin je met een kopie van dinsdag en verwijder je de namen van maandag. Dat geeft {Eva}. Verschil heeft een richting; als je die omdraait, verandert de vraag.
Controleer deelverzamelingen en gelijkheid
monday.containsAll(group) vraagt of elke waarde in group op maandag aanwezig is. Voor {Ben, Dee} geeft dit true terug: die groep is een deelverzameling van maandag. Het vereist niet dat maandag alleen die twee personen bevat.
Twee verzamelingen zijn gelijk als hun elementen overeenkomen, ongeacht de invoegvolgorde of implementatie. Een HashSet met “x” en “y” is gelijk aan een TreeSet met “y” en “x”. Lidmaatschap bepaalt de gelijkheid van verzamelingen, dus alleen de volgorde kan ze niet ongelijk maken.
Kies een volgorde voor het doorlopen
| Implementatie | Volgorde bij het doorlopen van elementen |
|---|---|
HashSet | Geen garantie |
LinkedHashSet | Invoegvolgorde |
TreeSet | Gesorteerde volgorde |
Alle drie voorkomen duplicaten. Gebruik LinkedHashSet als van belang is wanneer elke naam voor het eerst voorkwam. Gebruik TreeSet als elke doorloop al gesorteerd moet zijn. Voor strings volgt natuurlijk sorteren de tekencodes: kleine letters en hoofdletters gedragen zich niet als één hoofdletterongevoelig alfabet.
Een TreeSet met zijn standaardconstructor heeft elementen nodig die een natuurlijke vergelijking hebben, zoals strings, getallen of objecten die Comparable implementeren. In die verzameling markeert een vergelijkingsresultaat van nul een duplicaat. Een klasse die geen natuurlijke vergelijking biedt, veroorzaakt bij het invoegen een ClassCastException. Een expliciete comparator meegeven is een andere manier om de volgorde van een boomcollectie te definiëren; de standaardvoorbeelden gebruiken de natuurlijke volgorde.
Belangrijkste punten
addAll,retainAllenremoveAllwijzigen de verzameling waarop ze worden aangeroepen.- Kopieer een verzameling eerst als het oorspronkelijke lidmaatschap nog nodig is.
containsAllcontroleert een deelverzameling; gelijkheid van verzamelingen negeert de volgorde.- Kies hash-, linked- of tree-implementaties voor respectievelijk een niet-gespecificeerde volgorde, invoegvolgorde of gesorteerde volgorde.
4Programmeer met de interfaces List, Set en Map
Het type links in een collectiedeclaratie vertelt de rest van het programma welke bewerkingen het kan gebruiken. De klasse rechts biedt die bewerkingen en kan een bepaalde volgorde garanderen. Als je beide kanten leert lezen, wordt collectiecode makkelijker te wijzigen en worden methoden makkelijker opnieuw te gebruiken. Het helpt ook verklaren waarom een lijst, een verzameling en een map zich verschillend gedragen, zelfs als ze vergelijkbare gegevens bevatten.
4.1 Declareer variabelen en parameters met interfacetypen
Een interface beschrijft bewerkingen die een klasse aanbiedt als die de interface implementeert. Met een interface kan een variabele beschrijven wat de code nodig heeft, terwijl de keuze voor de opslag bij een concrete klasse blijft. List, Set en Map zijn interfaces in java.util die met collecties te maken hebben.
Lees beide kanten van een declaratie
List<String> names = new ArrayList<>();
Set<String> tags = new HashSet<>();
Map<String, Integer> counts = new HashMap<>();
Links staat de declaratie van het type van de variabele. Rechts wordt een object gemaakt van een klasse die de interface implementeert. Importeer beide typen, zoals java.util.List en java.util.ArrayList. Je kunt niet new List<>() of new Map<>() schrijven: een interface is geen concreet object dat je kunt maken.
Code die counts gebruikt, kan put, get en entrySet aanroepen omdat Map die declareert. Als alfabetische sleutels van pas komen, verander je de constructie in new TreeMap<>(). Bestaande aanroepen compileren nog steeds. Hun volgorde kan veranderen, omdat implementaties verschillende garanties over volgorde kunnen geven; de interface maakt hun gedrag niet in alle opzichten gelijk.
Accepteer de functionaliteit die een methode nodig heeft
Een methode die alleen lange woorden telt, kan een Collection accepteren:
static int countLong(Collection<String> words, int min) {
int count = 0;
for (String word : words) {
if (word.length() >= min) {
count++;
}
}
return count;
}
Voor [“a”, “tree”, “house”] en een minimum van 4 geeft de methode 2 terug. Dezelfde methode kan een ArrayList, een LinkedList, een HashSet of List.of(...) ontvangen, omdat die alleen de elementen leest. Een verzameling kan duplicaten al hebben verwijderd en daardoor andere invoergegevens leveren dan een lijst.
Met een interfaceparameter voorkom je dat één specifieke opslagklasse vereist is terwijl de methode die niet nodig heeft. Als posities nodig zijn, accepteer je List. Als opzoeken via een sleutel nodig is, accepteer je Map.
Ken de grenzen van het gedeclareerde type
List<String> names = new ArrayList<>();
names.ensureCapacity(100); // does not compile
Het object is een ArrayList, maar List declareert ensureCapacity niet. De compiler controleert het gedeclareerde type van de variabele. Gebruik een concreet klassetype als de speciale bewerking daarvan echt nodig is; verander gewone lijstparameters niet alleen om ze bij de klasse van één aanroeper te laten passen.
Plaats Map naast Collection
List en Set breiden Collection uit, dat een groep elementen beschrijft. Deze interface declareert methoden zoals add, remove, contains, size, isEmpty, addAll en clear. Of een wijziging wordt ondersteund, hangt af van de werkelijke collectie.
Map breidt Collection niet uit. Een map slaat koppelingen op en biedt weergaven die collecties zijn: keySet() is een Set, values() is een Collection en entrySet() is een Set met sleutel-waardeparen. Geef counts.values() door aan een methode die een collectie waarden nodig heeft, niet counts zelf.
Een klasse kan meerdere interfaces implementeren. LinkedList implementeert zowel List als Deque, een interface voor een dubbelzijdige wachtrij. Je eigen klasse noemt de interfaces die ze belooft te implementeren na implements, zoals in class Member implements Comparable<Member>.
Belangrijkste punten
- Declareer links de benodigde interface en maak rechts een object van een klasse die die interface implementeert.
- Interfaceparameters accepteren verschillende implementaties met de benodigde bewerkingen.
- Via een variabele zijn alleen methoden beschikbaar die door het type zijn gedeclareerd.
ListenSetbreidenCollectionuit;Mapbiedt collectieweergaven.
4.2 Weet welke garanties List, Set en Map bieden
Kies een interface op basis van de regels die je gegevens nodig hebben. Inhoud die op elkaar lijkt, maakt een lijst, een verzameling en een map niet onderling uitwisselbaar: elk geeft een andere betekenis aan posities, herhaling en gelijkheid.
Vergelijk de garanties
| Interface | Wat wordt opgeslagen | Belangrijke regel |
|---|---|---|
List | Elementen op posities vanaf 0 | Herhaalde elementen zijn toegestaan |
Set | Unieke elementen | Geen get-bewerking met een index |
Map | Sleutel-waardeparen | Sleutels zijn uniek; waarden mogen vaker voorkomen |
ArrayList en LinkedList implementeren List. HashSet, LinkedHashSet en TreeSet implementeren Set. HashMap, LinkedHashMap en TreeMap implementeren Map. Hashvarianten garanderen geen volgorde bij het doorlopen; linked-varianten behouden de invoegvolgorde; tree-varianten houden elementen of sleutels gesorteerd. Als je een bestaande sleutel opnieuw plaatst in een gewone LinkedHashMap met invoegvolgorde, behoudt die sleutel zijn plaats.
Lees verwijdering uit een lijst met gehele getallen zorgvuldig
Een List<Integer> heeft twee relevante overladen methoden:
List<Integer> numbers = new ArrayList<>(List.of(1, 5, 7));
numbers.remove(1);
Het argument 1 is een int, dus verwijdert Java het element op index 1: de waarde 5. Het resultaat is [1, 7]. Als je opnieuw begint met [1, 5, 7], kiest numbers.remove(Integer.valueOf(1)) voor verwijdering op waarde en blijft [5, 7] over. Het wrapperobject maakt duidelijk welke overladen methode is bedoeld.
Vergelijk inhoud met de juiste regel
Twee lijsten zijn gelijk als dezelfde elementen in dezelfde volgorde voorkomen. Een ArrayList [1, 2] is gelijk aan een LinkedList [1, 2], maar niet aan een lijst [2, 1]. Twee verzamelingen vergelijken het lidmaatschap en negeren de volgorde. Twee maps vergelijken koppelingen, dus beïnvloedt de invoegvolgorde hun gelijkheid niet. Hun concrete klassen kunnen verschillen terwijl hun inhoud volgens de regel van de interface gelijk is.
Herken niet-wijzigbare collecties
List<String> labels = List.of("a", "b");
Set<Integer> levels = Set.of(1, 2, 3);
Map<String, Double> prices = Map.of("tea", 2.0, "coffee", 2.5);
Deze fabrieksmethoden geven niet-wijzigbare collecties terug. Pogingen om de inhoud toe te voegen, te verwijderen, te vervangen of te sorteren werpen een UnsupportedOperationException op. Map.of accepteert maximaal tien sleutel-waardeparen.
Alle drie wijzen ze null-elementen, -sleutels of -waarden af met een NullPointerException. Set.of wijst dubbele elementen af en Map.of dubbele sleutels, met een IllegalArgumentException. List.of staat duplicaten toe omdat een lijst een element mag herhalen. De iteratievolgorde van de verzamelingen en maps die deze fabrieksmethoden maken, is niet gespecificeerd.
Kopieer als wijzigingen nodig zijn
new ArrayList<>(labels) maakt een wijzigbare lijst met dezelfde elementen. new HashSet<>(someList) en new HashMap<>(someMap) maken op dezelfde manier afzonderlijke collectiestructuren.
Deze kopieën zijn ondiep: ze bevatten dezelfde elementreferenties. Als je een element uit een kopie verwijdert, blijft de oorspronkelijke structuur intact, maar als je via een van beide collecties een veranderlijk object wijzigt, verandert het gedeelde object. Een niet-wijzigbare collectie verhindert wijzigingen aan de structuur; die bevriest niet elk object dat erin is opgeslagen.
Belangrijkste punten
- Lijsten hebben posities en duplicaten; verzamelingen hebben unieke elementen; maps hebben unieke sleutels.
remove(1)op een lijst met gehele getallen verwijdert op index;remove(Integer.valueOf(1))verwijdert op waarde.- Gelijkheid van lijsten controleert de volgorde; gelijkheid van verzamelingen en maps controleert de inhoud zonder de invoegvolgorde.
- Collecties van
of-fabrieksmethoden zijn niet wijzigbaar en wijzen null-waarden af. Set.ofwijst dubbele elementen af;Map.ofwijst dubbele sleutels af.- Een wijzigbare kopie heeft een aparte collectiestructuur, maar deelt de elementreferenties.
5Sorteer collecties met Comparable
Sorteren plaatst waarden in een volgorde die andere code kan gebruiken. Getallen en strings kennen hun natuurlijke volgorde al; objecten van je eigen klassen hebben een expliciete regel nodig. Volg een lijst tijdens een sortering en schrijf daarna een vergelijking die veilig omgaat met getallen, tekst en gelijke sorteerrangen. De regel is ook buiten lijsten van belang: een gesorteerde verzameling of map gebruikt die eveneens om te bepalen wanneer twee elementen of sleutels dezelfde plaats innemen.
5.1 Sorteer lijsten met getallen en strings
Een lijst behoudt een volgorde, maar die volgorde kan eenvoudigweg weergeven hoe waarden zijn binnengekomen. Sorteren herschikt de lijst met een vergelijkingsregel. Java biedt al een natuurlijke volgorde voor getallen en strings.
Sorteer een wijzigbare lijst ter plaatse
List<Double> values = new ArrayList<>(List.of(3.5, -1.0, 2.25));
Collections.sort(values);
Importeer java.util.Collections. Dezelfde lijst bevat nu [-1,0; 2,25; 3,5]. De sortering geeft void terug, dus values = Collections.sort(values) compileert niet. values.sort(null) sorteert ook in natuurlijke volgorde. Gebruik voor een array Arrays.sort(array); een array met 5, 2 en 8 wordt 2, 5, 8.
Voorspel de stringvolgorde teken voor teken
Bij het vergelijken van strings wordt naar het eerste verschillende teken gekeken. Als de ene string een prefix van de andere is, komt de kortere eerst. Zo komt “appel” vóór “appels”.
De lijst [“pear”, “Banana”, “apple”, “10”, “9”] wordt gesorteerd tot [“10”, “9”, “Banana”, “apple”, “pear”]. Voor deze tekens komen cijfers vóór hoofdletters en hoofdletters vóór kleine letters. “10” komt vóór “9” omdat ‘1’ vóór ‘9’ komt; de stringvolgorde interpreteert cijferstrings niet als gehele getallen. Sla numerieke gegevens op als getallen wanneer een numerieke volgorde nodig is.
Gebruik kant-en-klare alternatieve volgorden
names.sort(String.CASE_INSENSITIVE_ORDER);
values.sort(Collections.reverseOrder());
De eerste aanroep ordent [“pear”, “Banana”, “apple”] als [“apple”, “Banana”, “pear”] zonder de opgeslagen hoofdletters en kleine letters te wijzigen. De tweede gebruikt aflopende natuurlijke volgorde. De getallen 4, 9 en 1 worden 9, 4 en 1. Deze argumenten zijn comparators, objecten die een alternatieve volgorde bieden.
Collections.reverse(list) keert alleen de huidige reeks om. Dit levert een aflopende volgorde op als die reeks al oplopend was gesorteerd; op zichzelf sorteert het geen willekeurige lijst.
Behoud de volgorde bij gelijke sorteerrangen
Het sorteren van lijsten is stabiel: elementen die bij vergelijking gelijk zijn, behouden hun eerdere onderlinge volgorde. Als een vergelijking die alleen scores gebruikt twee personen als gelijk beoordeelt, blijft hun eerdere volgorde in de lijst behouden. Stabiliteit bewaart die invoervolgorde; er ontstaat niet vanzelf een aanvullend alfabetisch sorteercriterium.
Herken fouten en handige methoden
Een lijst van List.of(...) kan niet ter plaatse worden gesorteerd. Kopieer die eerst naar een ArrayList. Voor een klasse zonder Comparable mislukt de typecontrole van Collections.sort(list) bij het compileren. Als je list.sort(null) op zulke objecten aanroept, mislukt dat tijdens de uitvoering met een ClassCastException wanneer een vergelijking nodig is.
Collections.max(collection) en Collections.min(collection) vinden de grootste en kleinste elementen volgens de natuurlijke volgorde zonder de collectie te herschikken. Ze werpen een NoSuchElementException op voor een lege collectie.
Voor gesorteerde mapsleutels kun je map.keySet() naar een ArrayList kopiëren en de kopie sorteren, of een TreeMap gebruiken. Het sorteren van een gekopieerde sleutellijst laat de eigen iteratievolgorde van de map ongewijzigd.
Belangrijkste punten
Collections.sortwijzigt een wijzigbare lijst ter plaatse en geeftvoidterug.- De natuurlijke volgorde van strings vergelijkt tekens, niet de numerieke waarden in tekst.
- Gebruik kant-en-klare comparators voor een omgekeerde of hoofdletterongevoelige volgorde.
- Stabiel sorteren behoudt de eerdere onderlinge volgorde van elementen met gelijke sorteerrang.
- Kopieer een niet-wijzigbare lijst voordat je die sorteert.
minenmaxvinden extrema zonder te sorteren.
5.2 Implementeer Comparable in je eigen klasse
Java kan niet raden of een lid eerst op naam, lengte of een ander veld moet komen. Implementeer Comparable om de klasse één natuurlijke volgorde te geven. Sorteercode kan dan elk object vragen zichzelf te vergelijken met een ander object van die klasse.
Schrijf de vergelijkingsmethode
class Member implements Comparable<Member> {
private String name;
private int height;
Member(String name, int height) {
this.name = name;
this.height = height;
}
@Override
public int compareTo(Member other) {
return Integer.compare(this.height, other.height);
}
}
De lengte wordt gemeten in centimeters. Comparable<Member> geeft aan dat het andere object een Member is; een expliciete typeconversie is niet nodig. public int compareTo(Member other) is de vereiste methode. De standaardinterface Comparable staat in java.lang, dus is daarvoor geen import nodig.
Bij a.compareTo(b) plaatst een negatief resultaat a vóór b, zet nul ze op dezelfde vergelijkingspositie en plaatst een positief resultaat a na b. Alleen het teken is van belang: -35 is een even geldig negatief resultaat als -1.
Volg de volgorde
Met de vergelijking op lengte worden Ana 172, Ben 165 en Cai 180 gesorteerd tot Ben 165, Ana 172 en Cai 180. Verwissel de argumenten om langere leden eerst te plaatsen:
return Integer.compare(other.height, this.height);
Verander de regel bewust. Als je de argumenten omdraait, keer je de sorteerrichting van dat veld om; je verandert niet de betekenis van lengte of de gegevens van een lid.
Vergelijk gehele numerieke waarden veilig
Aftrekken lijkt handig, maar kan het verkeerde teken teruggeven als een int overloopt. Voor -2.000.000.000 en 2.000.000.000 geeft het aftrekken van het tweede getal van het eerste na overloop 294.967.296, een positief resultaat. Integer.compare geeft terecht een negatief resultaat terug.
Gebruik voor velden met kommagetallen Double.compare(this.price, other.price). Een verschil expliciet omzetten naar int laat informatie verloren gaan: (int) (1.8 - 1.5) is 0, zodat ongelijke prijzen dezelfde sorteerrang krijgen. Een vergelijkingsmethode behoudt de volgorde zonder het verschil af te kappen.
Vergelijk tekst en gebruik de regel opnieuw
Geef voor een volgorde op naam this.name.compareTo(other.name) terug. Deze methode volgt de tekenvolgorde van de string. compareToIgnoreCase biedt een hoofdletterongevoelige tekstvergelijking wanneer dat de bedoelde regel voor het veld is.
Zodra een klasse een natuurlijke volgorde definieert, kunnen bewerkingen zoals Collections.sort, Collections.max en de standaardvergelijkingen van TreeSet of TreeMap die gebruiken. Een klasse kan ook andere interfaces implementeren, gescheiden door komma’s na implements.
Er is één natuurlijke volgorde voor de klasse; er wordt niet voor elke lijst een andere compareTo gekozen. Gebruik aparte comparatorobjecten wanneer de ene context een volgorde op naam nodig heeft en de andere een volgorde op lengte. Een vergelijking die alleen lengte gebruikt, geeft ook alle gelijke lengtes dezelfde sorteerrang; of dat aanvaardbaar is, wordt vooral belangrijk bij gesorteerde verzamelingen.
Belangrijkste punten
- Implementeer
Comparable<YourClass>met een publieke methodecompareTo. - Negatieve resultaten, nul en positieve resultaten betekenen respectievelijk ervoor, gelijke sorteerrang en erna.
- Gebruik
Integer.compareenDouble.compareom getallen veilig te ordenen. - Verwissel vergelijkingsargumenten om de sorteerrichting van een veld om te keren.
- Eén klasse heeft één natuurlijke volgorde; comparators bieden andere volgorden.
5.3 Gebruik aanvullende sorteercriteria en werk met gesorteerde verzamelingen en maps
Een primaire vergelijking laat vaak gelijke sorteerrangen over. Twee films kunnen hetzelfde jaar hebben of twee spelers dezelfde score. Een aanvullend sorteercriterium geeft het volgende veld pas een beurt als de eerdere velden bij vergelijking gelijk zijn.
Vergelijk velden in volgorde van prioriteit
@Override
public int compareTo(Film other) {
int byYear = Integer.compare(this.year, other.year);
if (byYear != 0) {
return byYear;
}
return this.title.compareTo(other.title);
}
Het jaar is de hoofdregel. De titel is alleen van belang als de jaren gelijk zijn. Up (2009), Avatar (2009) en Alien (1979) worden gesorteerd tot Alien (1979), Avatar (2009), Up (2009). Als je eerst titels vergelijkt, krijgen die voorrang op het jaar en voer je een andere sorteertaak uit.
De richtingen kunnen per veld verschillen. Voor de hoogste punten eerst en namen van A tot Z begin je met Integer.compare(other.points, this.points). Geef bij nul this.name.compareTo(other.name) terug. Als je de hele uiteindelijke lijst zou omkeren, zou je ook de namen binnen gelijke scores omkeren.
Begrijp de duplicaatregel van een gesorteerde verzameling
Een standaard TreeSet<Member> gebruikt de natuurlijke vergelijking zowel om elementen te ordenen als om te bepalen wanneer een element al aanwezig is. Als Member.compareTo alleen lengte gebruikt, levert het vergelijken van Ana 172 en Dee 172 nul op. Het toevoegen van Ana geeft true terug; het toevoegen van Dee geeft false terug. Ana blijft het opgeslagen element, ook als equals de twee leden als verschillend beschouwt.
Een lijst kan beide objecten met gelijke sorteerrang behouden; een tree-verzameling geeft ze één vergelijkingspositie. Voeg het nodige aanvullende sorteercriterium toe als ze afzonderlijk in de verzameling moeten staan.
Stem vergelijking af op gelijkheid
Een volgorde is consistent met equals wanneer de vergelijking nul teruggeeft dan en slechts dan als equals true teruggeeft. Voor een klasse waarvan de gelijkheid lengte en naam gebruikt, kan vergelijken op lengte en daarna naam aan deze regel voldoen. Als gelijkheid een andere identiteit gebruikt, zoals een lid-ID, zorgt alleen naam toevoegen als aanvullend sorteercriterium niet voor consistentie.
De vergelijking moet zich ook gedragen als een geldige volgorde. Als a vóór b komt, moet de omgekeerde vergelijking b na a plaatsen. Als a vóór b komt en b vóór c, moet a vóór c komen. Vergelijkingsmethoden voor hele velden gebruiken in een vaste volgorde van prioriteit ondersteunt deze regels. Een inconsistente methode kan resultaten verkeerd ordenen of ervoor zorgen dat een sortering een IllegalArgumentException opwerpt.
Houd ook mapsleutels gesorteerd
Een TreeMap sorteert sleutels, geen waarden. Voor de woordtellingen van “de kat zag de hond en de kat rende” is de eerste sleutel “de” en de laatste “zag”. firstKey() en lastKey() geven die uiterste sleutels. Declareer een TreeMap of een passende interface voor gesorteerde maps wanneer je methoden aanroept die de gewone Map niet declareert.
De sleutelvergelijking bepaalt ook welke sleutels overeenkomen. Een tweede sleutel die bij vergelijking nul oplevert, werkt de bestaande koppeling bij, ook als equals de objecten onderscheidt. Standaard hebben tree-collecties elementen of sleutels nodig die van nature vergelijkbaar zijn; als je een object zonder geschikte vergelijking invoegt, wordt een ClassCastException opgeworpen. Een expliciet meegegeven comparator kan een andere volgorde definiëren.
Belangrijkste punten
- Vergelijk eerst het primaire veld en raadpleeg latere velden alleen bij een gelijke sorteerrang.
- Keer alleen de velden om die een aflopende volgorde nodig hebben.
- Tree-collecties behandelen vergelijkingsresultaat nul als hetzelfde element of dezelfde sleutel.
- Consistentie met
equalsvereist vergelijkingsresultaat nul precies wanneer gelijkheid waar is. TreeMapsorteert sleutels; de methoden voor de uiteinden geven de uiterste sleutels terug.- Een vergelijking moet tekens consequent omkeren en de volgorde over drietallen behouden.
6Kies de juiste collectie voor een taak
De keuze voor een collectie begint bij de vragen die het programma moet beantwoorden: op positie, op lidmaatschap of via een aparte sleutel. Volgorde en duplicaten beperken de keuze verder. Als de gegevens eenmaal bij de taak passen, vergelijk je hoe de veelgebruikte bewerkingen groeien naarmate er meer waarden bijkomen. Een kleine verandering, zoals één verzameling maken vóór een lus, kan herhaalde zoekacties wegnemen zonder het programma moeilijker leesbaar te maken.
6.1 Vergelijk de kosten van veelgebruikte collectiebewerkingen
De kosten van een bewerking beschrijven hoe het werk groeit met het aantal opgeslagen elementen. Zo kun je code herkennen die snel is voor een klein voorbeeld, maar traag wordt als de collectie groot wordt.
Vergelijk groei, geen seconden
Constante tijd, O(1), blijft ongeveer onafhankelijk van de collectiegrootte. Lineaire tijd, O(n), kan evenredig met het aantal elementen groeien. Logaritmische tijd, O(log n), groeit langzaam als de grootte toeneemt. Deze aanduidingen beschrijven groei, geen exacte uitvoeringstijden op een bepaalde computer.
| Collectie | Bewerking | Kosten |
|---|---|---|
ArrayList | get(i), set(i, x) | Constant |
ArrayList | Achteraan toevoegen | Gemiddeld constant |
ArrayList | contains, indexOf, verwijderen op waarde | Lineair |
ArrayList | Toevoegen of verwijderen nabij het begin | Lineair |
HashMap / HashSet | Gewone zoekactie op sleutel of lidmaatschapstest, toevoegen, verwijderen | Gemiddeld constant bij goed gespreide hashcodes |
TreeMap / TreeSet | Gewone zoekactie, toevoegen, verwijderen | Logaritmisch |
LinkedList | Toevoegen of verwijderen aan beide uiteinden | Constant |
LinkedList | get(i) in het algemeen | Lineair |
Een zoekactie in een ArrayList kan elk element controleren; wijzigingen aan het begin verschuiven latere elementen. Een LinkedList moet naar een positie lopen, dus herhaald benaderen via een index kan veel kosten. ArrayList is een bruikbare standaardkeuze voor gewoon werk met lijsten.
Herken het herhaald doorzoeken
Stel dat een programma 10.000 namen controleert tegen een lijst met 100.000 namen. Voor elke naam list.contains(name) aanroepen kan maximaal 1.000.000.000 gelijkheidscontroles vereisen. Een methodeaanroep die er snel uitziet, blijft bij dit gebruik een volledige zoekactie.
Maak één keer een verzameling voor lidmaatschap:
Set<String> allowed = new HashSet<>(allowedNames);
int matches = 0;
for (String name : incomingNames) {
if (allowed.contains(name)) {
matches++;
}
}
Het opbouwen van de verzameling met 100.000 elementen kost werk, gevolgd door 10.000 lidmaatschapscontroles die gemiddeld constante tijd kosten. Als je beide fasen meetelt, groeit het totaal ongeveer met de som van de twee invoergroottes, in plaats van met hun product. Als je de verzameling binnen de lus opnieuw zou opbouwen, herhaal je het opbouwwerk en gaat dat voordeel verloren.
Lees gemiddelde garanties correct
Hashbewerkingen vertrouwen op sleutels waarvan de hashcodes waarden goed spreiden. Voor elk object één constante hashcode teruggeven kan nog steeds voldoen aan de eisen voor correcte gelijkheid, maar maakt het opzoeken traag. Het is geen kosteloze manier om het schrijven van een bruikbare hashmethode te vermijden.
Tree-collecties behouden een gesorteerde volgorde met basisbewerkingen in logaritmische tijd. Een verdubbeling van het aantal elementen voegt maar een kleine hoeveelheid zoekwerk toe. Voor 100.000 elementen is een logaritme met grondtal twee ongeveer 16,6; dit geeft een indruk van de omvang van de zoekactie, geen exacte garantie van 17 vergelijkingen bij elke zoekactie in een Java-boom.
Houd rekening met de hele taak
Een extra verzameling opbouwen kost tijd en geheugen. Voor enkele elementen of één zoekactie kan de oorspronkelijke lijst voldoende zijn. Voor herhaalde lidmaatschapscontroles neemt één herbruikbare verzameling vaak de belangrijkste kosten weg. Begin bij het vereiste gedrag en onderzoek daarna de bewerkingen die het vaakst worden herhaald.
Belangrijkste punten
- Groeiaanduidingen beschrijven hoe de kosten meegroeien, geen gemeten aantal seconden.
ArrayListbiedt snelle toegang via een index, maar zoekacties en wijzigingen aan het begin kosten lineaire tijd.- Zoekacties met hashing kosten gemiddeld constante tijd bij geschikte hashcodes; zoekacties in een boom kosten logaritmische tijd.
- Bouw één keer een verzameling voor lidmaatschap om herhaald doorzoeken van lijsten te vervangen.
- Neem opbouwtijd en geheugengebruik mee wanneer je volledige oplossingen vergelijkt.
6.2 Kies een collectie die bij de taak past
De juiste collectie bewaart de informatie die het programma nodig heeft en maakt de belangrijkste vragen eenvoudig uit te drukken. Bepaal de regels voor de gegevens voordat je een vertrouwde klassenaam kiest.
Vraag wat een element identificeert
| Belangrijkste behoefte | Eerste keuze | Voorbeeld |
|---|---|---|
| Een waarde vinden via een aparte sleutel | Map | Productcode naar prijs |
| Lidmaatschap testen zonder duplicaten | Set | Bezette gebruikersnamen |
| Posities, volgorde of herhaalde elementen behouden | List | Een afspeellijst met herhaalde nummers |
| Een vast aantal plaatsen behouden | Array | Twaalf maandelijkse neerslagmetingen |
Een double[12] kan de neerslag in millimeters bevatten wanneer van elke plaats de maand bekend is. Een map met alleen posities 0, 1, 2 enzovoort als sleutels voegt een laag toe die een lijst of array al biedt.
Kies de volgorde die je nodig hebt
Gebruik voor maps en verzamelingen HashMap of HashSet als geen volgorde vereist is, LinkedHashMap of LinkedHashSet voor invoegvolgorde en TreeMap of TreeSet voor gesorteerde sleutels of elementen. Begin voor een lijst voor algemeen gebruik met ArrayList.
Alfabetisch weergegeven woordtellingen passen bij een TreeMap<String, Integer>. Een lijst met unieke labels in de volgorde waarin ze voor het eerst voorkwamen, past bij LinkedHashSet<String>. Herhaalde nummers vereisen een lijst, ook als de nummers elders in een verzameling zijn opgeslagen: herhalingen weggooien zou de afspeellijst veranderen.
Combineer collecties als de gegevens uit lagen bestaan
Studenten per cursus kunnen een Map<String, List<String>> vormen; elke waarde is een geordende lijst met inschrijvingen. Labels per artikel kunnen een Map<String, Set<String>> vormen; elk artikel krijgt unieke labels. Een ranglijst kan een List<Player> zijn die is gesorteerd volgens de natuurlijke vergelijking van de speler.
De keuze voor de buitenste en binnenste collectie beantwoordt verschillende vragen. Als cursussen alfabetisch moeten worden vermeld en studenten in volgorde van inschrijving moeten worden bewaard, gebruik dan buiten een TreeMap en binnen een lijst. Het sorteren van de sleutels sorteert de lijsten niet. Als dubbele inschrijvingen moeten worden genegeerd, gebruik dan een verzameling voor de binnenste groepen.
Houd verwante gegevens bij elkaar
Twee parallelle lijsten, één voor namen en één voor telefoonnummers, vertrouwen op overeenkomende posities. Als je een naam zonder het bijbehorende nummer verwijdert, klopt elke latere koppeling niet meer. Als namen personen uniek identificeren, kan een map de koppeling bewaren. Als namen vaker voorkomen of meerdere velden bij één persoon horen, bewaar dan objecten die personen voorstellen in een lijst of indexeer ze op een unieke ID.
Een lijst steeds opnieuw op een veld doorzoeken is een ander signaal om een indexmap te overwegen. Bewaar de lijst ook als weergavevolgorde en herhaalde elementen van belang zijn, maar werk beide structuren samen bij wanneer gegevens worden toegevoegd of verwijderd.
Controleer de elementregels en het gedeclareerde type
Gebruik een interfacedeclaratie zoals Map<String, Double> prices = new HashMap<>(); zodat aanroepers kunnen vertrouwen op de mapbewerkingen die ze nodig hebben. Hashcollecties hebben zinvolle gelijkheid en bijpassende hashcodes nodig voor zelfgemaakte sleutels of elementen. Standaard hebben tree-collecties een natuurlijke vergelijking nodig die precies de waarden onderscheidt die de collectie moet bewaren. Deze regels horen bij het laten weergeven van de bedoelde gegevens door de gekozen collectie.
Belangrijkste punten
- Kies maps om op sleutel te zoeken, verzamelingen voor lidmaatschap en lijsten voor posities of herhalingen.
- Arrays kunnen een vast aantal plaatsen bevatten, ook met primitieve waarden.
- Kies bewust voor invoegvolgorde of gesorteerde volgorde.
- Met geneste collecties kunnen buitenste sleutels en binnenste groepen verschillende regels volgen.
- Houd verwante velden bij elkaar en onderhoud meerdere indexen via gemeenschappelijke methoden.
- Stem gelijkheids- en vergelijkingsregels af op de waarden die de collectie moet bewaren.
HashMaps en collecties in Java: voorbeelden
1Volg een voorraadmap van opslaan tot opzoeken
13 stappen
- 1IntroductieBegin met een vraag over een product
Een voorraadprogramma moet de vraag “Hoeveel mappen zijn beschikbaar?” kunnen beantwoorden. Een map maakt de productnaam de sleutel en de hoeveelheid de waarde. Volg één map tijdens een vervanging, een zoekactie naar een ontbrekende sleutel en het opschonen. Bekijk daarna waarom een zelfgedefinieerde sleutel kan falen, ook als de velden er juist uitzien. Elke zoekactie hangt af van wat Java als dezelfde sleutel beschouwt.
- 2UitlegMaak één koppeling per sleutel
Maak
HashMap<String, Integer> stock = new HashMap<>();.Stringbeschrijft de sleutels;Integerbeschrijft de waarden. De map gebruikt referentietypen, dusintkan niet het typeargument voor de waarde zijn. Java zet een literal zoals6via autoboxing om wanneerstock.put("folder", 6)die opslaat. Met de diamantnotatie rechts kan Java de twee typen afleiden. - 3VoorbeeldVervang een hoeveelheid
Voer deze bijwerkingen op volgorde uit:
stock.put("folder", 6); stock.put("clip", 11); Integer old = stock.put("folder", 8);De laatste aanroep vindt de bestaande sleutel voor mappen, slaat
8op en geeft6terug inold. Er zijn nog steeds twee sleutels. Een map bewaart de meest recente waarde voor een sleutel; die telt hoeveelheden niet automatisch op en houdt er geen geschiedenis van bij. - 4UitdagingLees zonder toe te voegen
Wat zijn
nenstock.size()na deze leesactie? De map bevat nog steeds map 8 en paperclip 11.int n = stock.getOrDefault("tape", 0);---reveal---
nis0en de grootte blijft2. De standaardwaarde is een resultaat voor een ontbrekende sleutel, geen toegevoegd sleutel-waardepaar. Als je in plaats daarvan leest metget("tape"), krijg je null terug, wat niet via unboxing kan worden omgezet naar eenint. - 5UitlegLees sleutels en waarden samen
Een lus over sleutel-waardeparen biedt elke sleutel samen met de waarde:
for (Map.Entry<String, Integer> e : stock.entrySet()) { System.out.println(e.getKey() + ": " + e.getValue()); }Importeer
java.util.Map. De lus kan beide koppelingen tonen zonder een aparteget. Als alleen een totaal nodig is, doorloop jestock.values()en tel je de hoeveelheden op; herhaalde hoeveelheden dragen nog steeds afzonderlijk bij. - 6KernpuntLeid het eerste paar niet af
De uitvoer kan map vóór paperclip of paperclip vóór map vermelden. HashMap garandeert geen van beide volgorden. Een resultaat zoals een som wordt daardoor niet beïnvloed, maar een regel als “bewaar de eerste score bij een gelijke stand” kan afhangen van de volgorde van doorlopen. Kies
LinkedHashMapvoor invoegvolgorde ofTreeMapvoor gesorteerde sleutels en geef gelijke maxima een expliciete regel als de winnaar voorspelbaar moet zijn. - 7VoorbeeldVerwijder tijdens het doorlopen
Stel dat map nu 0 is en paperclip 11. Verwijder hoeveelheden van nul met een iterator:
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator(); while (it.hasNext()) { if (it.next().getValue() == 0) { it.remove(); } }De iterator verwijdert het huidige sleutel-waardepaar en gaat veilig verder. Als je
stock.removeaanroept vanuit een for-each-doorloop, kan dat eenConcurrentModificationExceptionveroorzaken. - 8UitdagingMaak een aparte kopie van de sleutels
Vóór het opschonen wordt
keysgemaakt metnew ArrayList<>(stock.keySet()). Wat gebeurt er met de voorraadmap alskeys.remove("folder")wordt aangeroepen?---reveal---
De map behoudt de koppeling voor mappen.
keysis een aparte lijst met de sleutels die bestonden toen de kopie werd gemaakt. Als je iets uitstock.keySet()zelf zou verwijderen, zou het sleutel-waardepaar uit de map verdwijnen, omdat dat object een gekoppelde weergave is. - 9VoorbeeldProbeer een zelfgedefinieerde sleutel
Stel dat
Binde veldenrowencolumnheeft en geen gelijkheidsmethoden overschrijft. Sla een label op metnew Bin(3, 4)en zoek het daarna op met een anderenew Bin(3, 4). De zoekactie geeft null terug. Dit zijn twee verschillende objecten en de overgeërfdeObject.equalsgebruikt identiteit. Overeenkomende veldwaarden zijn nog niet vastgelegd als regel voor overeenkomende sleutels. - 10UitlegStem gelijkheid en hashcodes op elkaar af
Voor identiteit van bakken op basis van coördinaten overschrijf je
equals(Object other)om rij en kolom te vergelijken en overschrijf jehashCode()met dezelfde velden, bijvoorbeeldObjects.hash(row, column). Gelijke coördinaten moeten gelijke hashcodes opleveren. Een overeenkomende hashcode alleen stelt geen gelijkheid vast; de gelijkheidscontrole onderscheidt nog steeds verschillende sleutels op basis van coördinaten. - 11UitdagingHerken een overladen methode
Een klasse definieert
public boolean equals(Bin other)enhashCode()op basis van haar coördinaten. Waarom kan een zoekactie met een afzonderlijke bak die gelijk lijkt toch mislukken?---reveal---
De parameter moet
Objectzijn om de gelijkheidsmethode van Java te overschrijven. De methode met een Bin-parameter is een overladen methode, waardoor de overgeërfde gelijkheid op basis van identiteit blijft gelden. Als je@Overrideaan die verkeerde signatuur toevoegt, maakt de compiler de fout zichtbaar. - 12UitdagingHerstel een ontwerp met een veranderende sleutel
Een baksleutel gebruikt rij en kolom in zowel de gelijkheidsmethode als de hashcodemethode. Het programma wijzigt de rij terwijl de sleutel is opgeslagen en kan daarna het label niet vinden. Hoe moet het programma de locatie bijwerken en daarna lege labels veilig verwijderen tijdens het doorlopen?
---reveal---
Verwijder de koppeling voordat je de sleutel wijzigt en voeg die daarna opnieuw toe. Gebruik voor het opschonen tijdens het doorlopen de eigen verwijdermethode van de iterator over sleutel-waardeparen. Stabiele sleutelidentiteit en veilig doorlopen lossen verschillende delen van deze bijwerking op.
- 13SamenvattingHoud een controlelijst voor mapbijwerkingen bij
Bepaal bij een ontbrekend zoekresultaat of een standaardwaarde of een expliciete controle op afwezigheid bij de taak past. Bepaal bij een lus of de volgorde van belang is en of sleutels worden verwijderd. Controleer bij een zelfgedefinieerde sleutel de exacte signatuur
equals(Object), overeenkomstige hashvelden en stabiele identiteit. Deze controles vinden verschillende oorzaken van hetzelfde probleem: een koppeling die het programma niet kan gebruiken zoals verwacht.
2Bouw aanwezigheidsinformatie op terwijl records binnenkomen
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieZet binnenkomende gegevens om in bruikbare antwoorden
- 2VoorbeeldBegin een sessietelling
- 3VoorbeeldOnderscheid unieke sessies van records
- 4UitdagingHerstel een telling die wordt teruggezet
- 5UitlegMaak een groep één keer aan
- 6VoorbeeldVolg de binnenste referentie
- 7UitdagingZoek de fout met een gedeelde groep
- 8UitlegIndexeer op een identificerende sleutel
- 9KernpuntWijzig één object op meerdere plaatsen
- 10UitdagingHoud verwijderingen consistent
- 11UitdagingCombineer tellingen met uniek lidmaatschap
- 12SamenvattingControleer een eerste invoerwaarde en een herhaling
3Vergelijk twee groepen aanwezigen
9 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVraag wie aanwezig was, niet hoe vaak
- 2VoorbeeldVoeg herhaalde scans samen
- 3UitdagingStop bij de eerste herhaalde invoerwaarde
- 4VoorbeeldBehoud alleen gemeenschappelijke leden
- 5UitlegKies de richting van het verschil
- 6UitdagingTest een geselecteerde groep
- 7TipKies een bruikbare volgorde voor het overzicht
- 8UitdagingMaak een gesorteerd overzicht van afwezigen
- 9SamenvattingLees de ontvangende verzameling als begingroep
4Lees collectiedeclaraties en kopieën
10 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieLees beide kanten van de declaratie
- 2UitlegDeclareer wat de aanroeper nodig heeft
- 3VoorbeeldGebruik een ruimere parameter om te lezen
- 4UitdagingOnderzoek de ontbrekende methode
- 5VerbandKies de betekenis van de inhoud
- 6VoorbeeldVolg verwijdering bij gehele getallen
- 7UitdagingKopieer voordat je sorteert
- 8KernpuntOnderscheid objectwijzigingen van lijstwijzigingen
- 9UitdagingGeef de juiste mapweergave door
- 10SamenvattingControleer type, bewerking en object
5Maak een ranglijst die de juiste gelijke scores behoudt
15 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieKies wat eerst komt
- 2VoorbeeldSorteer dezelfde lijst
- 3UitlegLees het eerste verschillende teken
- 4UitdagingGebruik een aangeboden volgorde
- 5UitlegGeef een klasse één natuurlijke volgorde
- 6VoorbeeldVoorkom een onterechte gelijke sorteerrang bij getallen
- 7UitdagingKeer het numerieke veld om
- 8VoorbeeldGebruik alleen bij echte gelijke standen een aanvullend criterium
- 9VoorbeeldDoorloop de rangschikking
- 10UitdagingBekijk wat een tree-verzameling behoudt
- 11KernpuntStem volgorde af op identiteit
- 12VerbandControleer de sleutel van een gesorteerde map
- 13UitdagingBehoud elk resultaat in een gesorteerde lijst
- 14TipLaat de regel werken voor drietallen
- 15SamenvattingControleer een vergelijking op twee plaatsen
6Kies structuren voor een cursuslijst
10 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieMaak een lijst van de vragen van het programma
- 2VoorbeeldHerken de zoekactie binnen de lus
- 3UitlegMaak één keer een verzameling voor lidmaatschap
- 4UitdagingVergelijk groeiende invoer
- 5VoorbeeldBehoud herhaalde inschrijvingsrecords
- 6UitlegKies de buitenste en binnenste volgorde
- 7UitdagingHerstel de verkeerde duplicaatregel
- 8TipHoud rekening met de kosten van de gewenste volgorde
- 9UitdagingCombineer het uiteindelijke ontwerp
- 10SamenvattingKies gedrag voordat je snelheid meet
HashMaps en collecties in Java: oefenvragen
Set 1: Lees maps betrouwbaar en werk ze bij10 Oefenvragen
Vraag 1 van 10
HashMap<String, Integer> ontvangt put("brush", 5) en daarna put("brush", 14). Wat zijn de grootte en de waarde voor "brush"?Toon antwoordVerberg antwoord
put telt de nieuwe hoeveelheid niet op bij de vorige hoeveelheid. Voor optellen is een expressie nodig die leest en bijwerkt.Vraag 2 van 10
"tray". Wat gebeurt er wanneer int n = map.get("tray"); wordt uitgevoerd?Toon antwoordVerberg antwoord
get voor een ontbrekende sleutel geeft null terug en bij het toewijzen van dat resultaat aan een int probeert Java het via unboxing om te zetten. Null heeft geen primitieve gehele waarde, dus werpt Java NullPointerException op. De map levert geen nul en maakt geen hoeveelheid aan alleen omdat de doelvariabele numeriek is.Vraag 3 van 10
Toon antwoordVerberg antwoord
entrySet() biedt een sleutel-waardepaar met zowel een sleutel als de bijbehorende waarde. Een lus over sleutels kan de waarde met een extra zoekactie ophalen, maar het element uit de sleutelverzameling zelf biedt alleen de sleutel. De waardenweergave kan niet aangeven welke sleutel bij elke status hoort.Vraag 4 van 10
equals. Wat moeten hun hashcodes zijn?Toon antwoordVerberg antwoord
Vraag 5 van 10
"beam" → 9. Het programma voert Integer old = map.put("beam", 13); uit en daarna Integer removed = map.remove("beam");. Wat zijn old, removed en de uiteindelijke grootte?Toon antwoordVerberg antwoord
put teruggeeft met de waarde die de methode opslaat.Vraag 6 van 10
entrySet() en moet elk paar met waarde nul verwijderen. Hoe moet de lus het paar tijdens het doorlopen verwijderen?Toon antwoordVerberg antwoord
ConcurrentModificationException veroorzaken. Een hoeveelheid door null vervangen zou het paar niet verwijderen.Vraag 7 van 10
Cell definieert equals(Cell other) en een hashCode op basis van velden. Afzonderlijke cellen met gelijke velden komen niet overeen als sleutels in een HashMap. Welke wijziging aan een methode verhelpt de gelijkheidsfout?Toon antwoordVerberg antwoord
equals(Object), dus is de methode met een Cell-parameter een overladen methode en blijft de overgeërfde gelijkheid op basis van identiteit gelden. Overschrijf de methode met een Object-parameter en gebruik overeenkomstige velden voor de hashcode. Alleen de hashcode wijzigen zorgt er niet voor dat de verkeerde gelijkheidsmethode bij het opzoeken wordt gebruikt.Vraag 8 van 10
Toon antwoordVerberg antwoord
Vraag 9 van 10
"ink" → 4 en "paper" → 0. keys is een nieuwe ArrayList die uit de sleutelverzameling is gekopieerd. Het programma verwijdert "ink" uit keys, verwijdert "paper" via de sleutelverzameling van de map en verdubbelt daarna alle resterende mapwaarden. Wat blijft in de map over?Toon antwoordVerberg antwoord
Vraag 10 van 10
Location gebruiken een veranderlijke coördinaat. Een sleutel wordt opgeslagen en daarna wordt de coördinaat gewijzigd terwijl de sleutel is opgeslagen; de koppeling wordt nu niet gevonden. Welke volgorde van bijwerken moet het ontwerp gebruiken om dit te voorkomen?Toon antwoordVerberg antwoord
Set 2: Bouw tellingen op en vergelijk groepen10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
"north" → 4, "south" → 2 en "west" → 3. Hoeveel verschillende sleutels bevat de map?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
"cedar". Wat geeft een nieuwe add("cedar") terug?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
"red", "gold", "red", "blue", "gold", "red" met counts.put(word, counts.getOrDefault(word, 0) + 1). Wat zijn het aantal voor red en de grootte van de map?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
groups.put(team, new ArrayList<>()); uitgevoerd, gevolgd door groups.get(team).add(name);. Een team ontvangt Omar, Pia en daarna Ren. Wat blijft onder dat team over?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
C.removeAll(B) uit. Wat bevat C?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Device is opgeslagen in een lijst en een ID-map. Het wordt uit de map opgehaald en daarna verandert het statusveld. Welke status toont de opgeslagen referentie in de lijst?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
!seen.add(n). Welk getal wordt gemeld?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
"Morning" als "Evening". Het programma voegt "Lee" toe via Morning, vervangt daarna de koppeling van Evening door een nieuwe lijst en voegt "Jo" toe via Evening. Wat zijn de uiteindelijke groepen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
retainAll(B) uit. D wordt gekopieerd uit B en voert removeAll(C) uit. Wat zijn D en het resultaat van A.containsAll(C)?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Gebruik interfaces en sorteer gegevens10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Set<String> codes = new HashSet<>(); het gedeclareerde type van codes?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
a.compareTo(b) teruggeeft?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
List<Integer> begint als [4, 8, 12]. De lijst voert remove(1) uit. Wat blijft over?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
"zebra", "Apron", "18", "3", "ant"?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Collection<String>. Een aanroeper heeft Map<String, String> labels en wil dat de methode elke labelwaarde verwerkt. Wat moet worden doorgegeven?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
(int) (this.price - other.price) terug. Wat gaat er mis voor prijzen 6,8 en 6,3?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
compareTo daarna doen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Ticket. Daarvan wordt een tweede ArrayList gekopieerd. Het programma wijzigt het label van het ticket via de kopie en maakt daarna de kopie leeg. Wat bevat de oorspronkelijke lijst?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Reading.equals gebruikt station-ID en temperatuur, maar de natuurlijke vergelijking gebruikt alleen temperatuur. Een standaard TreeSet ontvangt A met 16, B met 16 en daarna C met 12. Wat zijn de grootte en de behouden ID’s?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Onderzoek het gedrag van collecties10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
"pending" die expliciet aan null is gekoppeld. Wat geeft getOrDefault("pending", 20) terug?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Set.of("draft", "final", "draft") wordt geëvalueerd?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
"Ari Chen" worden ingevoegd en het eerste lid kan niet meer via die naam worden opgehaald. Welk ontwerp behoudt beide leden direct voor afzonderlijke zoekacties?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
Collections.reverse(list)?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
"Support". Het programma evalueert List<String> result = groups.getOrDefault("Support", new ArrayList<>()); en voegt daarna "Nia" toe aan result. Wat geeft een latere groups.get("Support") terug?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
ScoreKey gebruikt team en punten; de natuurlijke vergelijking gebruikt alleen punten. Een standaard TreeMap krijgt de sleutel East-30 met waarde "old", West-30 met waarde "new" en daarna North-18 met waarde "low". Wat zijn de grootte en de waarde die met East-30 wordt gevonden?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
addAll(B) uit en daarna removeAll(A). Wat is U en is een van beide originelen veranderd?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 5: Combineer opzoeken, groeperen en volgorde10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
map.values() optelt?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
get. Welke volledige oplossing behoudt de vereisten?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
Aanvullend sorteercriterium
Afkappen
Array
double[12] kan elke maand één plaats geven voor de neerslagmeting in millimeters.Autoboxing
12 in een map met Integer-waarden plaatst, zet Java dat getal automatisch via autoboxing om voor opslag.C
Collectiekeuze
Collectieweergave
Collection
Collection<String> accepteren en een lijst of een verzameling doorlopen.Comparable
compareTo te implementeren.Comparable<Member> implementeren zodat lijstsortering haar instanties op lengte kan ordenen.Comparator
String.CASE_INSENSITIVE_ORDER sorteert een lijst zonder de hoofdletters en kleine letters in elke opgeslagen string te wijzigen.compareTo
Consistentie met equals
Constante tijd
ArrayList lezen vereist niet dat alle eerdere posities in die lijst worden doorzocht.D
Deelverzameling
a.containsAll(b) test of b een deelverzameling van a is.Diamantnotatie
HashMap<String, Integer> m = new HashMap<>(); voorkomt de diamantnotatie dat de twee typen worden herhaald.Doorsnede
retainAll verwijdert niet-gedeelde waarden uit de ontvangende verzameling.E
Encapsulatie
addBook die samen bijwerkt in plaats van aanroepers ze te laten bewerken.F
Frequentietabel
G
Gedeclareerd type
ArrayList die in een List-variabele is opgeslagen, maakt ensureCapacity niet via die variabele beschikbaar.Gedeelde referentie
Gelijkheid op basis van identiteit
Object wordt geërfd.Geneste collectie
getOrDefault
Groeperen
H
Hashcodecontract
equals gelijk zijn, gelijke hashcodes teruggeven, maar staat hashbotsingen voor ongelijke objecten toe.x en y in zowel hun gelijkheidstest als hun hashcodemethode.HashMap
HashMap<String, Integer> kan het huidige aantal van een product opzoeken zonder een lijstpositie te gebruiken.HashSet
HashSet<String> kan bijhouden welke namen al zijn verwerkt terwijl een programma een langere invoerreeks leest.Herhaalde invoerwaarde
add false teruggeeft.I
Implementatie
HashMap en TreeMap implementeren allebei Map, maar geven verschillende garanties over de sleutelvolgorde.Index
Inhoudelijke gelijkheid
equals(Object).Interface
List<String> accepteert, kan een ArrayList of een LinkedList ontvangen zonder de signatuur te wijzigen.Iteratievolgorde
Iterator
hasNext(), next() en de remove() van de iterator om hoeveelheden van nul te verwijderen.L
Lidmaatschap
contains dat test met de vergelijkingsregel van die verzameling.Lineaire tijd
LinkedHashMap
LinkedHashMap met invoegvolgorde.LinkedHashSet
LinkedHashSet wordt gekopieerd, verliest herhalingen maar behoudt de volgorde waarin labels voor het eerst voorkwamen.List
Logaritmische tijd
Lopend totaal
M
Map
Map.Entry
N
Natuurlijke volgorde
"20" en "3" sorteert, komt "20" eerst omdat de eerste tekens hun volgorde bepalen.Niet-wijzigbare collectie
List.of moet vóór het sorteren worden gekopieerd, maar een opgeslagen veranderlijk object wordt door die lijst niet bevroren.O
Ondiepe kopie
Opbouwkosten
Opzoeken op sleutel
Opzoektabel
Overladen methodevariant
remove(1) verwijdering op index, terwijl remove(Integer.valueOf(1)) verwijdering op waarde kiest.Overloop bij gehele getallen
Integer.compare voorkomt de tekenomkering die aftrekken kan veroorzaken.P
Parallelle lijsten
Parameter van een interfacetype
Map<String, Integer> accepteren terwijl aanroepers opslag met een hashstructuur of boomstructuur kiezen.putIfAbsent
putIfAbsent ervoor zorgen dat de sleutel een lijst heeft zonder een bestaande lijst te vervangen.S
Sleutel-waardepaar
"pen" → 18 een productnaam aan het aantal dat momenteel beschikbaar is.Sleutelnormalisatie
Stabiele sortering
Structurele wijziging
T
Tekenvolgorde
"7", "Bee" en "ant" plaatst de natuurlijke stringvolgorde het cijfer vóór de letters.Ter plaatse sorteren
Collections.sort(values) toont de bestaande lijstvariabele de elementen in hun nieuwe volgorde.Transitiviteit
TreeMap
firstKey().TreeSet
U
Unboxing
Integer-zoekresultaat aan een int toewijzen mislukt omdat de teruggegeven null niet via unboxing kan worden omgezet.Uniek element
"sun rain sun" bevat twee unieke woordsleutels, maar registreert drie voorkomens.Unieke identificator
V
Veranderlijke sleutel
Vereniging
addAll vormt de vereniging door de ontvangende verzameling te wijzigen.Verschil van verzamelingen
Verzameling
W
Wrapperklasse
Integer als waardetype omdat int niet als generiek typeargument kan voorkomen.- 1Eerste Java-programma’s
- 2Voorwaarden en lussen in Java
- 3Java-methoden
- 4Lijsten, arrays en strings in Java
- 5Java-klassen en objecten
- 6Objecten, referenties en gelijkheid in Java
- 7HashMaps en collecties in Java
- 8Overerving in Java
- 9Excepties en bestanden in Java
- 10Klassendiagrammen en unittests in Java
- 11Lambda-expressies en streams in Java
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.