In uw Java-toepassingen werkt u doorgaans met verschillende soorten objecten. En u wilt misschien bewerkingen uitvoeren zoals sorteren, zoeken en itereren op deze objecten.
Voor de introductie van het Collections-framework in JDK 1.2 zou u Arrays en Vectors hebben gebruikt om een groep objecten op te slaan en te beheren. Maar ze hadden hun eigen nadelen.
Het Java Collections Framework heeft als doel deze problemen te overwinnen door hoogwaardige implementaties van veelvoorkomende datastructuren te bieden. Deze stellen u in staat om u te concentreren op het schrijven van de applicatielogica in plaats van op laag-niveau bewerkingen.
De introductie van Generics in JDK 1.5 verbeterde het Java Collections Framework aanzienlijk. Generics stellen u in staat om typeveiligheid af te dwingen voor objecten die in een collectie zijn opgeslagen, wat de robuustheid van uw applicaties vergroot. U kunt hier meer lezen over Java Generics hier.
In dit artikel zal ik u begeleiden in het gebruik van het Java Collections Framework. We zullen de verschillende soorten collecties bespreken, zoals Lijsten, Sets, Wachtrijen en Kaarten. Ik zal ook een korte uitleg geven van hun belangrijkste kenmerken, zoals:
-
Interne mechanismen
-
Omgaan met duplicaten
-
Ondersteuning voor null-waarden
-
Volgorde
-
Synchronisatie
-
Prestatie
-
Belangrijke methoden
-
Veelvoorkomende implementaties
We zullen ook enkele codevoorbeelden doornemen voor een beter begrip, en ik zal het hebben over de Collections utility class en het gebruik ervan.
Inhoudsopgave:
Begrijpen van het Java Collections Framework
Volgens de Java documentatie is “Een collectie is een object dat een groep objecten vertegenwoordigt. Een collectiestructuur is een eenduidige architectuur voor het vertegenwoordigen en manipuleren van collecties.”
In eenvoudige termen helpt het Java Collections Framework je om een groep objecten te beheren en efficiënt en georganiseerd bewerkingen op hen uit te voeren. Het maakt het gemakkelijker om applicaties te ontwikkelen door verschillende methoden aan te bieden voor het omgaan met groepen objecten. Je kunt objecten effectief toevoegen, verwijderen, doorzoeken en sorteren met behulp van het Java Collections Framework.
Collectie-interfaces
In Java specificeert een interface een contract dat moet worden nageleefd door elke klasse die het implementeert. Dit betekent dat de implementeerde klasse concrete implementaties moet bieden voor alle methoden die in de interface zijn verklaard.
In het Java Collections Framework breiden verschillende collectie-interfaces zoals Set
, List
en Queue
de Collection
interface uit, en ze moeten voldoen aan het contract dat door de Collection
interface is gedefinieerd.
Decoderen van de Java Collections Framework Hiërarchie
Bekijk dit nette diagram uit dit artikel dat de Java Collectie Hiërarchie illustreert:
We beginnen van boven en werken naar beneden zodat je kunt begrijpen wat dit diagram laat zien:
-
Aan de basis van het Java Collections Framework staat de
Iterable
interface, die je in staat stelt om over de elementen van een collectie te itereren. -
De
Collection
interface breidt deIterable
interface uit. Dit betekent dat het de eigenschappen en het gedrag van deIterable
interface erft en zijn eigen gedrag toevoegt voor het toevoegen, verwijderen en ophalen van elementen. -
Specifieke interfaces zoals
List
,Set
enQueue
breiden deCollection
interface verder uit. Elke van deze interfaces heeft andere klassen die hun methoden implementeren. Bijvoorbeeld,ArrayList
is een populaire implementatie van deList
interface,HashSet
implementeert deSet
interface, enzovoort. -
De
Map
interface maakt deel uit van het Java Collections Framework, maar breidt deCollection
interface niet uit, in tegenstelling tot de hierboven genoemde anderen. -
Alle interfaces en klassen in dit framework maken deel uit van het
java.util
pakket.
Opmerking: Een veelvoorkomende bron van verwarring in het Java Collections Framework draait om het verschil tussen Collection
en Collections
. Collection
is een interface in het framework, terwijl Collections
een utility-klasse is. De Collections
-klasse biedt statische methoden die bewerkingen uitvoeren op de elementen van een collectie.
Java Collection Interfaces
U bent inmiddels bekend met de verschillende soorten collecties die de basis vormen van het collecties-framework. Nu zullen we een nadere blik werpen op de List
, Set
, Queue
en Map
interfaces.
In deze sectie bespreken we elk van deze interfaces terwijl we hun interne mechanismen verkennen. We zullen onderzoeken hoe ze omgaan met dubbele elementen en of ze de invoer van null-waarden ondersteunen. We begrijpen ook de volgorde van elementen tijdens invoer en hun ondersteuning voor synchronisatie, die betrekking heeft op het concept van threadveiligheid. Vervolgens zullen we een paar sleutelmethoden van deze interfaces doorlopen en afsluiten met een beoordeling van veelvoorkomende implementaties en hun prestaties voor verschillende bewerkingen.
Voordat we beginnen, laten we kort praten over Synchronisatie en Prestatie.
-
Synchronisatie beheert de toegang tot gedeelde objecten door meerdere threads, waardoor hun integriteit wordt gewaarborgd en conflicten worden voorkomen. Dit is cruciaal voor het handhaven van threadveiligheid.
-
Bij het kiezen van een collectie type is een belangrijke factor de prestaties tijdens gangbare operaties zoals invoegen, verwijderen en ophalen. Prestaties worden meestal uitgedrukt met behulp van Big-O notatie. Je kunt er meer over leren hier.
Lijsten
Een Lijst
is een geordende of sequentiële verzameling van elementen. Het volgt zero-based indexering, waardoor de elementen kunnen worden ingevoegd, verwijderd of benaderd met behulp van hun indexpositie.
-
Interne werking: Een
Lijst
wordt intern ondersteund door ofwel een array of een gekoppelde lijst, afhankelijk van het type implementatie. Bijvoorbeeld, eenArrayList
gebruikt een array, terwijl eenLinkedList
intern een gekoppelde lijst gebruikt. Je kunt er meer over lezen hier. EenLijst
past zich dynamisch aan bij het toevoegen of verwijderen van elementen. De op indexering gebaseerde toegang maakt het een zeer efficiënte type verzameling. -
Duplicaten: Duplicaat-elementen zijn toegestaan in een
List
, wat betekent dat er meer dan één element in eenList
kan zijn met dezelfde waarde. Elke waarde kan worden opgehaald op basis van de index waarop deze is opgeslagen. -
Null: Null-waarden zijn ook toegestaan in een
List
. Aangezien duplicaten zijn toegestaan, kun je ook meerdere null-elementen hebben. -
Volgorde: Een
List
behoudt de volgorde van invoer, wat betekent dat de elementen worden opgeslagen in dezelfde volgorde als ze zijn toegevoegd. Dit is handig wanneer je elementen wilt ophalen in de exacte volgorde waarin ze zijn ingevoerd. -
Synchronisatie: Een
List
is standaard niet gesynchroniseerd, wat betekent dat het geen ingebouwde manier heeft om toegang door meerdere threads tegelijkertijd te verwerken. -
Belangrijke methoden: Hier zijn enkele belangrijke methoden van een
List
interface:add(E element)
,get(int index)
,set(int index, E element)
,remove(int index)
, ensize()
. Laten we kijken hoe we deze methoden kunnen gebruiken met een voorbeeldprogramma.import java.util.ArrayList; import java.util.List; public class ListExample { public static void main(String[] args) { // Maak een lijst List<String> list = new ArrayList<>(); // add(E element) list.add("Appel"); list.add("Banaan"); list.add("Kers"); // get(int index) String secondElement = list.get(1); // "Banaan" // set(int index, E element) list.set(1, "Blauwe bes"); // remove(int index) list.remove(0); // Verwijdert "Appel" // size() int size = list.size(); // 2 // Print de lijst System.out.println(list); // Uitvoer: [Blauwe bes, Kers] // Print de grootte van de lijst System.out.println(size); // Uitvoer: 2 } }
-
Veelvoorkomende implementaties:
ArrayList
,LinkedList
,Vector
,Stack
-
Prestaties: Typisch zijn invoer- en verwijderbewerkingen snel in zowel
ArrayList
alsLinkedList
. Maar het ophalen van elementen kan traag zijn omdat je door de knooppunten moet navigeren.
Bewerking | ArrayList | LinkedList |
Invoeren | Snel aan het einde – O(1) gemiddeld, traag aan het begin of in het midden – O(n) | Snel aan het begin of in het midden – O(1), traag aan het einde – O(n) |
Verwijderen | Snel aan het einde – O(1) gemiddeld, traag aan het begin of in het midden – O(n) | Snel – O(1) als de positie bekend is |
Ophalen | Snel – O(1) voor willekeurige toegang | Traag – O(n) voor willekeurige toegang, aangezien dit navigeren inhoudt |
Verzamelingen
Een Set
is een type collectie dat geen duplicaat elementen toestaat en het concept van een wiskundige verzameling vertegenwoordigt.
-
Interne werking: Een
Set
wordt intern ondersteund door eenHashMap
. Afhankelijk van het type implementatie wordt het ondersteund door eenHashMap
,LinkedHashMap
of eenTreeMap
. Ik heb een gedetailleerd artikel geschreven over hoeHashMap
intern werkt hier. Zorg ervoor dat je het bekijkt. -
Dupliceer elementen: Aangezien een
Set
het concept van een wiskundige verzameling vertegenwoordigt, zijn dubbele elementen niet toegestaan. Dit zorgt ervoor dat alle elementen uniek zijn, waardoor de integriteit van de verzameling behouden blijft. -
Null: Een maximaal aantal van één null-waarde is toegestaan in een
Set
omdat duplicaten niet zijn toegestaan. Maar dit geldt niet voor deTreeSet
implementatie, waar null-waarden helemaal niet zijn toegestaan. -
Ordening: De ordening van elementen in een
Set
hangt af van het type implementatie.-
HashSet
: De volgorde is niet gegarandeerd, en elementen kunnen op elke positie worden geplaatst. -
LinkedHashSet
: Deze implementatie behoudt de volgorde van invoer, zodat je de elementen in dezelfde volgorde kunt ophalen als waarin ze zijn ingevoerd. -
TreeSet
: Elementen worden ingevoegd op basis van hun natuurlijke volgorde. Alternatief kun je de invoervolgorde beheersen door een aangepaste comparator op te geven.
-
-
Synchronisatie: Een
Set
is niet gesynchroniseerd, wat betekent dat je mogelijk tegen gelijktijdigheidsproblemen aanloopt, zoals racecondities, die de gegevensintegriteit kunnen beïnvloeden als twee of meer threads tegelijkertijd proberen toegang te krijgen tot eenSet
object. -
Belangrijke methoden: Hier zijn enkele belangrijke methoden van een
Set
interface:add(E element)
,remove(Object o)
,contains(Object o)
, ensize()
. Laten we kijken hoe we deze methoden kunnen gebruiken met een voorbeeldprogramma.import java.util.HashSet; import java.util.Set; public class SetExample { public static void main(String[] args) { // Maak een set Set<String> set = new HashSet<>(); // Voeg elementen toe aan de set set.add("Apple"); set.add("Banana"); set.add("Cherry"); // Verwijder een element uit de set set.remove("Banana"); // Controleer of de set een element bevat boolean containsApple = set.contains("Apple"); System.out.println("Bevat Apple: " + containsApple); // Krijg de grootte van de set int size = set.size(); System.out.println("Grootte van de set: " + size); } }
-
Veelvoorkomende implementaties:
HashSet
,LinkedHashSet
,TreeSet
-
Prestaties:
Set
implementaties bieden snelle prestaties voor basisbewerkingen, behalve voor eenTreeSet
, waar de prestaties relatief langzamer kunnen zijn omdat de interne datastructuur het sorteren van de elementen tijdens deze bewerkingen omvat.
Bewerking | HashSet | LinkedHashSet | TreeSet |
Invoegen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Verwijderen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Ophalen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Queues
Een Queue
is een lineaire verzameling van elementen die wordt gebruikt om meerdere items vast te houden voordat ze worden verwerkt, meestal volgens de FIFO (first-in-first-out) volgorde. Dit betekent dat elementen aan de ene kant worden toegevoegd en aan de andere kant worden verwijderd, zodat het eerste element dat aan de wachtrij is toegevoegd, het eerste is dat wordt verwijderd.
-
Intern mechanisme: De interne werking van een
Queue
kan verschillen op basis van de specifieke implementatie.-
LinkedList
– gebruikt een dubbel gekoppelde lijst om elementen op te slaan, wat betekent dat je zowel vooruit als achteruit kunt doorlopen, wat flexibele bewerkingen mogelijk maakt. -
PriorityQueue
– wordt intern ondersteund door een binaire heap, wat zeer efficiënt is voor opvraagoperaties. -
ArrayDeque
– is geïmplementeerd met behulp van een array die uitbreidt of krimpt naarmate elementen worden toegevoegd of verwijderd. Hier kunnen elementen aan beide uiteinden van de wachtrij worden toegevoegd of verwijderd.
-
-
Duplicates: In een
Queue
zijn dubbele elementen toegestaan, waardoor meerdere instanties van dezelfde waarde kunnen worden ingevoegd. -
Null: Je kunt geen null-waarde invoegen in een
Queue
omdat sommige methoden van eenQueue
ontworpen zijn om null terug te geven om aan te geven dat deze leeg is. Om verwarring te voorkomen, zijn null-waarden niet toegestaan. -
Ordening: Elementen worden ingevoegd op basis van hun natuurlijke volgorde. Als alternatief kunt u de invoegvolgorde regelen door een aangepaste vergelijker op te geven.
-
Synchronisatie: Een
Queue
is standaard niet gesynchroniseerd. Maar u kunt eenConcurrentLinkedQueue
of eenBlockingQueue
implementatie gebruiken voor het bereiken van threadveiligheid. -
Belangrijke methoden: Hier zijn enkele belangrijke methoden van een
Queue
interface:add(E element)
,offer(E element)
,poll()
, enpeek()
. Laten we kijken hoe we deze methoden kunnen gebruiken met een voorbeeldprogramma.import java.util.LinkedList; import java.util.Queue; public class QueueExample { public static void main(String[] args) { // Maak een wachtrij met behulp van LinkedList Queue<String> queue = new LinkedList<>(); // Gebruik de add-methode om elementen toe te voegen, gooit een uitzondering als de invoeging mislukt queue.add("Element1"); queue.add("Element2"); queue.add("Element3"); // Gebruik de offer-methode om elementen toe te voegen, geeft false terug als de invoeging mislukt queue.offer("Element4"); // Toon de wachtrij System.out.println("Wachtrij: " + queue); // Peek naar het eerste element (verwijdert het niet) String firstElement = queue.peek(); System.out.println("Peek: " + firstElement); // geeft "Element1" terug // Poll het eerste element (haalt het op en verwijdert het) String polledElement = queue.poll(); System.out.println("Poll: " + polledElement); // geeft "Element1" terug // Toon de wachtrij na de poll System.out.println("Wachtrij na poll: " + queue); } }
-
Veelvoorkomende implementaties:
LinkedList
,PriorityQueue
,ArrayDeque
-
Prestaties: Implementaties zoals
LinkedList
enArrayDeque
zijn meestal snel voor het toevoegen en verwijderen van items. DePriorityQueue
is iets langzamer omdat het items invoegt op basis van de ingestelde prioriteitsvolgorde.
Operatie | LinkedList | PriorityQueue | ArrayDeque |
Invoegen | Snel aan het begin of in het midden – O(1), traag aan het einde – O(n) | Trager – O(log n) | Snel – O(1), Traag – O(n), als het resizing van de interne array inhoudt |
Verwijderen | Snel – O(1) als de positie bekend is | Trager – O(log n) | Snel – O(1), Traag – O(n), als het resizing van de interne array inhoudt |
Ophalen | Traag – O(n) voor willekeurige toegang, omdat het traverseren inhoudt | Snel – O(1) | Snel – O(1) |
Kaarten
Een Map
vertegenwoordigt een verzameling van sleutel-waarde paren, waarbij elke sleutel naar een enkele waarde wijst. Hoewel Map
deel uitmaakt van het Java Collection framework, breidt het de java.util.Collection
interface niet uit.
-
Interne werking: Een
Map
werkt intern met behulp van eenHashTable
gebaseerd op het concept van hashing. Ik heb een gedetailleerd artikel over dit onderwerp geschreven, dus lees het voor een dieper begrip. -
Dupliceerwaarden: Een
Map
slaat gegevens op als sleutel-waarde paren. Hier is elke sleutel uniek, dus dubbele sleutels zijn niet toegestaan. Maar dubbele waarden zijn toegestaan. -
Null: Aangezien dubbele sleutels niet zijn toegestaan, kan een
Map
slechts één null-sleutel hebben. Aangezien dubbele waarden zijn toegestaan, kan het meerdere null-waarden hebben. In deTreeMap
implementatie kunnen sleutels niet null zijn omdat het de elementen op basis van de sleutels sorteert. Null-waarden zijn echter toegestaan. -
Ordening: De volgorde van invoer van een
Map
varieert afhankelijk van de implementatie:-
HashMap
– de invoervolgorde is niet gegarandeerd, aangezien deze is gebaseerd op het concept van hashing. -
LinkedHashMap
– de invoervolgorde wordt behouden en je kunt de elementen in dezelfde volgorde terughalen als waarin ze aan de collectie zijn toegevoegd. -
TreeMap
– Elementen worden ingevoegd op basis van hun natuurlijke volgorde. Alternatief kun je de invoervolgorde beheersen door een aangepaste comparator op te geven.
-
-
Synchronisatie: Een
Map
is standaard niet gesynchroniseerd. Maar je kuntCollections.synchronizedMap()
ofConcurrentHashMap
implementaties gebruiken om threadveiligheid te bereiken. -
Belangrijke methoden: Hier zijn enkele belangrijke methoden van een
Map
interface:put(K key, V value)
,get(Object key)
,remove(Object key)
,containsKey(Object key)
, enkeySet()
. Laten we kijken hoe we deze methoden kunnen gebruiken met een voorbeeldprogramma.import java.util.HashMap; import java.util.Map; import java.util.Set; public class MapMethodsExample { public static void main(String[] args) { // Maak een nieuwe HashMap aan Map<String, Integer> map = new HashMap<>(); // put(K key, V value) - Voegt sleutel-waarde paren toe aan de map map.put("Apple", 1); map.put("Banana", 2); map.put("Orange", 3); // get(Object key) - Geeft de waarde terug die aan de sleutel is gekoppeld Integer value = map.get("Banana"); System.out.println("Waarde voor 'Banana': " + value); // remove(Object key) - Verwijdert het sleutel-waarde paar voor de opgegeven sleutel map.remove("Orange"); // containsKey(Object key) - Controleert of de map de opgegeven sleutel bevat boolean hasApple = map.containsKey("Apple"); System.out.println("Bevat 'Apple': " + hasApple); // keySet() - Geeft een setweergave van de sleutels in de map terug Set<String> keys = map.keySet(); System.out.println("Sleutels in map: " + keys); } }
-
Veelvoorkomende implementaties:
HashMap
,LinkedHashMap
,TreeMap
,Hashtable
,ConcurrentHashMap
-
Prestaties: De
HashMap
implementatie wordt veel gebruikt, voornamelijk vanwege de efficiënte prestatiekenmerken zoals weergegeven in de onderstaande tabel.
Bewerking | HashMap | LinkedHashMap | TreeMap |
Invoegen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Verwijderen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Ophalen | Snel – O(1) | Snel – O(1) | Langzamer – O(log n) |
Collections Utility Class
Zoals aan het begin van dit artikel is benadrukt, heeft de Collections
utility klasse verschillende nuttige statische methoden waarmee je veelgebruikte bewerkingen op de elementen van een collectie kunt uitvoeren. Deze methoden helpen je de boilerplate-code in je applicatie te verminderen en laten je focussen op de bedrijfslogica.
Hier zijn enkele belangrijke kenmerken en methoden, samen met wat ze doen, kort vermeld:
-
Sorteer:
Collections.sort(List<T>)
– deze methode wordt gebruikt om de elementen van een lijst in oplopende volgorde te sorteren. -
Zoeken:
Collections.binarySearch(List<T>, key)
– deze methode wordt gebruikt om naar een specifiek element in een gesorteerde lijst te zoeken en de index ervan terug te geven. -
Omgekeerde volgorde:
Collections.reverse(List<T>)
– deze methode wordt gebruikt om de volgorde van elementen in een lijst om te keren. -
Min/Max Operaties:
Collections.min(Collection<T>)
enCollections.max(Collection<T>)
– deze methoden worden gebruikt om respectievelijk de minimum- en maximumelementen in een collectie te vinden. -
Synchronisatie:
Collections.synchronizedList(List<T>)
– deze methode wordt gebruikt om een lijst thread-veilig te maken door deze te synchroniseren. -
Niet-wijzigbare Collecties:
Collections.unmodifiableList(List<T>)
– deze methode wordt gebruikt om een alleen-lezen weergave van een lijst te creëren, waardoor wijzigingen worden voorkomen.
Hier is een voorbeeld van een Java-programma dat verschillende functionaliteiten van de Collections
utility klasse demonstreert:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(3);
numbers.add(8);
numbers.add(1);
// Sorteren
Collections.sort(numbers);
System.out.println("Sorted List: " + numbers);
// Zoeken
int index = Collections.binarySearch(numbers, 3);
System.out.println("Index of 3: " + index);
// Omgekeerde Volgorde
Collections.reverse(numbers);
System.out.println("Reversed List: " + numbers);
// Min/Max Operaties
int min = Collections.min(numbers);
int max = Collections.max(numbers);
System.out.println("Min: " + min + ", Max: " + max);
// Synchronisatie
List<Integer> synchronizedList = Collections.synchronizedList(numbers);
System.out.println("Synchronized List: " + synchronizedList);
// Niet-wijzigbare Collecties
List<Integer> unmodifiableList = Collections.unmodifiableList(numbers);
System.out.println("Unmodifiable List: " + unmodifiableList);
}
}
Dit programma demonstreert sorteren, zoeken, omkeren, het vinden van minimum- en maximumwaarden, synchroniseren en het creëren van een niet-wijzigbare lijst met behulp van de Collections
utility klasse.
Conclusie
In dit artikel heb je geleerd over het Java Collections Framework en hoe het helpt bij het beheren van groepen objecten in Java-toepassingen. We hebben verschillende soorten collecties verkend, zoals Lijsten, Sets, Queues en Maps, en inzicht gekregen in enkele van de belangrijkste kenmerken en hoe elk van deze types deze ondersteunt.
Je hebt geleerd over prestaties, synchronisatie en belangrijke methoden, en waardevolle inzichten opgedaan in het kiezen van de juiste datastructuren voor jouw behoeften.
Door deze concepten te begrijpen, kun je het Java Collections Framework volledig benutten, waardoor je efficiëntere code kunt schrijven en robuuste applicaties kunt bouwen.
Als je dit artikel interessant vond, voel je vrij om mijn andere artikelen op freeCodeCamp te bekijken en met me te verbinden op LinkedIn.
Source:
https://www.freecodecamp.org/news/java-collections-framework-reference-guide/