JAVA-B2
Collections
Collections Framework
Diagramm
Was sind Collections
Vorteile
Collection Interfaces
Core-Collection-Interfaces (Begriff, Ziele, Standard)
Liste der Core Interfaces
List
Set
Queue
Dequeue
SortedSet
NavigableSet
Map
SortedMap
NavigableMap
Collections Zusammenfassung
@!Beispiele+Links
Das Interface Collection
Das Superinterface
Diagramm
Diagramm
Collection-Standard-Methoden
Zusammenfassung
Implementierungsklassen
Implementierungsklassen
Diagramm
Array[T]>
Lists
ArrayList<E>
LinkedList<E>
CopyWriteOnList<E>
Set<E> Implementations
HashSet<E>
TreeSet<E>
EnumSet<E>
CopyWriteOnArraySet<E>
Queue<E> Implementations
LinkedList<E>
PriorityQueue<E>
Deque<E> Implementations
LinkedList<E>
ArrayDeque<E>
Map<E> Implementations
HashMap<K,V>
LinkedHashMap<K,V>
TreeMap<K,V>
WeakHashMap<K,V>
IdentityHashMap<K,V>
EnumMap<K,V>
ConcurrentHashMap<K,V>**
ConcurrentSkipList<K,V>**
SortedSet<E> Implementations
TreeSet<E>
EnumSet<E>
BlockingQueue<E> Implementations
ArrayBlockingQueue<E>
LinkedBlockingQueue<E>
LinkedBlockingQueue<E>
PriorityBlockingQueue<E>
SynchronousQueue<E>
DelayQueue<E>
Legacy Types für Listen
Vector
Hashtable
Stack
Stack
HashSet<E>
HashSet<E>
@!Descriptions
@!Examples
@!Index/Examples
Overview
Interface Collection | Overview
Das Interface «Collection» ist das Superinterface für alle weiteren Interfaces im Java Collection Framework und stellt dahingehend mit seinen Methoden auch den Standard für alle Implementierungsklassen dar.
Seit Java 8 wurde im Zusammenhang mit dem Java Collection Framework
die Stream API eingeführt und für alle Implementierungklassen über
das Collection-Interface durchgesetzt. Das Verständnis dieses
Interfaces Collection ist die
zwingende Voraussetzung für das Verständnis der Nutzer der übrigen
Interfaces und Klassen.
Super-Interface
Collection | Super-Interface
Die Kenntnis und ein Verständnis der Methoden dieses Super-Interface
Collection<E ist bedeutsam
für die Vermeidung von Programmierfehler und Denkfehlern.
Maßgebend für den grundlegenden Einstieg wird ein Verständnis der Bedeutung der von Collection abgeleiteten Interfaces wie mitunter List, Set oder Map sein. Auf diese wird in einem eigenen Themenblock eingegangen werden.
Abbildung: Grafik mit JAVA Collection-Interfaces und JAVA Collection-Klassen sowie Informationen zur Implementierung und Erweiterung von Interfaces um andere Interfaces, SNEWMEDIA, 2025. {@ref}
Sinn & Zweck
Collection | Sinn & Zweck
Das Interface «Collection» ist das Super- oder Basis-Interface für alle anderen Interfaces in der Gruppe der Core-Collection-Interfaces. Alle Methoden, welche also das Collection-Interface vorgibt, werden von allen durch Java bereit gestellten Collections sichergestellt.
Die Kenntnis und ein Verständnis dieser Methoden ist entscheidend für das Verständnis und das fehlerfreie Arbeiten mit Collections in Java. So wird in diesem Themenblock auch erwähnt, wie man kontrolliert und verlässlich Elemente einer Collection mit Iteratoren entfernt, weil alle anderen Varianten zu Fehlern führen können.
Eine Collection repräsentiert eine Gruppe von Objekts. Objekte einer Gruppe bezeichnet hierbei als Elemente (engl. elements).
Maximale Vereinheitlich von Collections
Das Collection-Interface wird verwendet, um Gruppen von Objekten in einer Programmanwendung zwischen Methoden, Funktionen und Variablen transportieren zu können. Es ist deshalb gewollte, dass alle Collections nach dem selben Prinzip zu verstehen und zu nutzen sind.
Konvention: Konstruktor hat ein Collection Argument
Mit dem Ziel einer maximalen Vereinheitlichung aller Collection-Interfaces und damit auch dem
Verhalten aller Implementierungsklassen wurde die Konvention geschaffen, dass eine jede Collection
stets ein Konstruktur-Argument vom Typ Constructor besitzt.
Man bezeichnet das auch als Conversion-Constructor.
Conversion Constructor
Der Sinn und Zweck eines Konstruktorarguments vom Typ Collection
besteht darin, dass eine jede Gruppe von Elementen, welche vom Objekt dieses Interfaces
verwaltet werden soll, von Beginn an mit einer bestehenden Collection mit all deren
Elementen initialisiert werden kann.
Egal, welchen Typ und damit welche Implementierungsklasse die im Konstruktor als Argument übergebene andere Collection mit dem einen oder anderen Collection-Sub-Interface auch gehabt hat: Eine jede Collection im Java-Collection-Framework ist in der Lage, die Gruppe der eigenen Elemente mit den Elementen der im Konstruktor gelieferten Elemente zu initialisieren.
Oder anders formuliert: Man kann mit Hilfe des Konstruktors einer jeden Collection und damit schlichtweg über die Verwendung von Collection-Interfaces vom Prinzip her den Typ einer Collection in einen anderen Typ ändern.
Beispiel: Umwandlung in ArrayList und List
Beispiel: Wenn wir irgendeine Collection von Vornamen haben sollten, so können wir diese Gruppe von String-Elementen allein über den Konstruktor der Klasse ArrayList in einen Typ umwandlen, welcher schlussendlich das List Interfaces implementiert hat.
Die ArrayList hat das dieses List-Interface implementiert.
// List, Set, oder andere Collection
Collection<String> c = Data.getVornamen();
List<String> list = new ArrayList<String>(c);
// Ab Java 7 ist auch eine Kurznotation
// mit dem sogenannten Diamond-Operator möglich.
List<String> list = new ArrayList<>(c);
Methoden
Collection | Methoden des Collection-Interfaces
Eine jede Collection verfügt über einen Mindestumfang praktischer Methoden. Das Collection-Interface als Basis-Interface stellt sicher, dass jedes Interface eine Reihe von Methoden bereitstellt, so dass das Arbeiten mit jeder Collection egal welcher Implementierung immer gleich aussieht.
So lange eine Collection eine Gruppe von Objekten als Elemente beinhaltet, macht eine jede Collection aus dem Java Collection Framework exakt das, was man erwartet.
Wir können mit size() die Größe bestimmen und mit isEmpty() + erfahren, ob die Sammlung leer ist. Und mit contains() können wir prüfen, ob das Objekt schon enthalten ist.
int size() Anzahl der Elemente
boolean isEmpty() Ist die Collection leer?
boolean contains(Object element) Ist dieses Objekt schon enthalten?
Die add() Methoden der Collections sind in der Lage, eigenständig zu erkennen, ob Gruppen auf doppelte Einträge geprüft und diese entfernt werden müssen oder ob diese Erhalten bleiben können, da manche Collections doppelte Elemente als Duplikate erlauben.
Die add() Methode der Collection stellt sicher, dass die Collection das Objekt bekommen wird, wenn es noch nicht bestand, und es haben wird, falls es bereits bestand und man das Objekt kein zweites Mal hinzugefügt hat, falls die Collection keine Dubplikate erlaubt. Add() liefert true wenn ein Element neu hinzugefügt wurde.
Auch die remove() Methode einer Collection ist so programmiert worden, dass Sie eine einzelne Instanz eines Objekts in der Collection entfernt. Wenn die Collection verändert wurde, liefert die remove() Methode true.
boolean add(E element) Hinzufügen eines Objekts als Element
boolean remove(Object element) Entfernen eines Objekts
Iterator<E> iterator() Iterator erhalten zum Durchlaufen aller Elemente.
Mit Hilfe eines Iterators, den wir über it=collection.iterator() bekommen, können wir über eine Abfolge von while, it.hasNext() und it.next() alle Elemente kontrolliert durchlaufen.
// Iterator erhalten zum Durchlaufen aller Elemente.
Iterator<E> iterator()
Hinweis: Es wird bei größeren Datenmengen empfohlen, die mit Java 8 hinzugekommenen Aggregate-Operations in Verbindung mit stream und parallelStream zu verwenden. Diese benötigen weniger Speicherplatz, der Quellcode ist kürzer, die Lesbarkeit von Programmcode hat sich verbessert, die Performance steigt und eine Reihe von Problemstellungen wurden entschärft.
Methoden als Operationen für komplette Collections.
boolean containsAll(Collection<?> c) Sind alle Elemente enthalten?
boolean addAll(Collection<? extends E> c) Komplette Gruppe hinzufügen
boolean removeAll(Collection<?> c) Elemente dieser Gruppe enfernen
boolean retainAll(Collection<?> c)
void clear() Alle Elemente der Gruppe löschen.
Operationen für Umwandlung von Collections in Arrays. Dieses wird oft
für die Konvertierung von Datenstrukturen für die Nutzung alter APIs verwendet,
welche noch mit Arrays und nicht mit Collections arbeiten..
Object[] toArray() Umwandeln der Collection in einen Object-Array
<T> T[] toArray(T[] a) Exportieren aller Elemente in einen Array des Typs T
Ab Java 8 wurden weitere Methoden im Zusammenhang mit Streams ermöglicht. Damit ist eine sequentielle Verarbeitung aller Elemente der Gruppe ebenso möglich wie eine parallele Verarbeitung. Mehr hierzu siehe: Aggregate Operations, Using Streams mit Stream<E> stream() und Stream<E> parallelStream().
Wer auch weiterhin gern wie in anderen Programmiersprachen wie in JavaScript oder PHP gern seine Elemente in Gruppen selbst verwalten will, kann das zwar auch in Java weiterhin machen. Die Realität sieht aber in Java so aus, dass alle Packages, die von Java selbst kommen, bereits konsequent Collections und entsprechende Interfaces verwenden. Man kommt am Java Collection Framework als faktisch nicht vorbei.
Traversing Collections
Für das Durchlaufen aller Elemente, die als Gruppe in einem Container-Objekt enthalten sind, dessen Typ das Collection-Interface implementiert hat, ist mitunter über 3 Varianten möglich: Aggregate-Operations, For-Each-Konstrukte und durch die Verwendung eines Iterators.
Die ab Java 8 empfohlende und dahingend auch übliche Variante über die Iteration über alle Elemente einer Collection ist die Beschaffung eines Streams mit anschließender Verwendung von Aggregate-Operationen.
Streams, Lambdas und Aggregate-Operations ab Java 8
Eine AggregateOperation wird zumeist in Verbindung mit Lambda-Expressions realisiert. Der zugehörige Programmcode ist oft lesbare, benötigt weniger Code-Zeilen und ist damit leichter verständlich. The following code sequentially iterates through a collection of shapes and prints out the red objects:
Das nachfolgende Beispiel zeigt, man sich für eine Collection zuerst einen
stream über die stream()
Methode beschafft.
Beim Durchlaufen der Sequenz aller Elemente wird der Strom von
Elementen über die filter() Funktion auf die Elemente
reduziert, welche die als Predicate
e->e.getColor()==Color.RED
formulierte Bedingng erfüllen.
Der letztendlich vom Filter durchgelassene Strom von Elementen wird
dann Element für Element mit Hilfe der forEach(..)
Aggregate-Operation auf der Konsole ausgeben.
farbenFormenCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
Es ist auch eine nebenläufige Variante mit parallelStream möglich.
Im Falle sehr großer Datenmengen und der Verfügbarkeit eines Rechners mit mehreren Prozessoren
ist leicht möglich, die Verarbeitung aller Elemente
einer Collection ohne die manuelle Programmierung
von Threads und Runnables sehr einfach über die
parallelStream() Methode
zu ermöglichen, über welche alle Collections des
Java Collection Frameworks verfügen.
farbenFormenCollection.parallelStream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
Hinweis: In Bezug auf die parallele Verarbeitung von Elementen und die Programmierung von Lambda-Expressions gilt es eine ganze Reihe von Aspekten zu berücksichtigen, so dass wir für dieses Thema eigene Themenblöcke und Kapitel geschaffen haben.
stream, map, collect, joining
Eine häufige Problemstellung besteht darin, eine Liste aller Werte als String zu erhalten und die Elemente hier bei durch einen Separator, beispielsweise ein Komma, zu trennen.
String joined = elements.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
Die stream-Methode liefert uns eine Sequenz von Elementen, ein Element nach dem anderen.
Um jedes Element der Liste in einen String umzuwandeln, soll
jedes Element-Objekt der Liste durch die toString(element) Methode
verarbeitet werden. Die map(..) Methode
erstellt also eine Map, wobei
hierfür eine Referenz auf eine Methode erforderlich ist.
Anstelle der Programmierung eines Lambda-Ausdrücks können also auch Referenzen auf Methoden programmiert werden.
Mit Hilfe der Aggregate-Operation collect(..)
wird nun ein Rückgabewert vom Typ String durch die
Collectors.joining(", ") Methode
realisiert. Da diese aber wissen muss, welcher Separator verwendet wird,
wird dieser noch in Klammern für ein Komma angegeben.
Summieren aller Gehälter
Das nachfolgende Beispiel zeigt, wie man quasi in einem Einzeiler die Summe der Gehälter aller Mitarbeiter berechnen kann.
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
Die stream() Methode sorgt dafür, dass die Aggregate-Operation
collect einen Stream als Input
mit einer Sequenz alle Elemente der Collection bekommt.
Jedes Elemente der Liste ist ein Employee
Element. Für den zu ermittelten Gesamtwert der Operation wird
die summingInt() Methode von
Collectors verwendet. Diese liest für jedes Element, was als
Employee-Objekt geliefert wird, über dessen getSalery()
das Gehalt des Employee-Objekts aus und fügt es entsprechend
zur bisherigen Summe hinzu.
Verändert eine Operation die Daten oder nicht?
Das Collection-Framework hatte bereits vor der Version Java 8 und damit vor der Erweiterung der Stream-API über ein paar sogenannter "Bulk-Operations" verfügt, dh. Operationen, mit deren Hilfe eine gesamte Gruppe von Elementen einer Collection verarbeitet werden konnte.
Die ursprünglich vorhandenen Operationen wie
containsAll,
addAll
oder auch
removeAll
konnten komplette Collections überarbeiten.
Es gibt aber einen ganz entscheidenen Unterschied zwischen
den alten Bulk-Operations oder Massen-Operationen auf der einen
Seite und den und den neuen mit JAVA 8 über Streams hinzugekommenen Aggregate-Operationen:
Alle alten Bulk-Operationen sind mutative, dh. diese Operationen modifizieren und verändern damit die diesen als Grundlage der Verabeitung von der Collection gelieferten Daten.
Im Gegenzug werden diese Daten der eigentlichen Collection durch die mit Java 8 neu hinzugekommenen Aggregate-Operationen NICHT verändert; zumindest werden diese theoretisch dann nicht verändert, wenn Entwickler bei der Programmierung von Lambda-Expressions darauf achten, solche Veränderungen (engl. Mutations) zu vermeiden.
Insbesondere mit Blick auf parallelStream sollte man dringend
darauf achten, dass alle Operationen, die man mit Lambda-Expressions und damit die sogenannte
Funktionale Programmierung steuert, auf keinen Fall Nebeneffekte, engl. Side-Effects,
haben sollten.
Aggregate-Operationen sollten bevorzugt genutzt werden
Aggregate-Operationen arbeiten wahlweise sequentiell mit Streams oder auch parallel, wenn parallelStream() verwendet wird.
Die Sequenz aller Elemente, die von einer Aggregate-Operation wie mitunter forEach und collect dann zu einem Ergebnis verarbeitet wird, lassen sich auf dem Weg dieser Verarbeitung noch filtern und über map() umwandeln.
Die neuen Aggregate-Operationen verändern NICHT die Original-Collection sondern liefern in Fällen, wo wiederum eine Collection geliefert werden würde, eine neue Collection.
Da die Verwendung von parallelStream letztendlich zu einer nebenläufigen Verarbeitung von Daten über mehrere Prozessoren erfolgt, sind in diesem Fall alle Problemstellungen zu berücksichtigen, die auch im Kontext von Threads zu klären sind. Dieses betrifft mitunter das Problem der Reihenfolge, in welcher Elemente verarbeitet werden, denn bei einer Nebenläufigkeit ist diese Reihenfolge nicht mehr gesichert.
"Traversing Collections" ist der Überbegriff wo wir ursprünglich von einem "Iterieren über Elemente der Collection" haben sprechen können. Wo früher primär mit einem Iterator gearbeitet wurde, bieten die Streams ab Java 8 einen alternativen, neuen, besseren und zu empfehlenden Ansatz.
forEach Konstrukt
Mit Hilfe der forEach-Operation ist es möglich, alle Elemente einer jeden Collection oder übrigens auch einem Array wie in einer for-Schleife zu durchlaufen.
for-Statement für Collections
Syntax: Der erste Wert entspricht dem Element der Collection, welches schrittweise beim Durchlaufen aller Elemente dann für eine Verarbeitung im eigentlichen Körper des for-Statements verarbeitet werden kann.
Der nach dem : folgende Wert ist die Referenz auf ein Objekt vom Typ einer Collection.
// Einzeiler
for (Object o : collection) System.out.println(o);
// Zweizeiler
for (Object o : collection)
System.out.println(o);
// Dreizeiler
for (Object o : collection) {
System.out.println(o);
}
Hinweis: Wenn es sich bei dem für jedes Element durchzuführende Code nur um einen einzigen Ausdruck handelt, können die geschweiften Klammern entfallen. Dieses ermöglicht im Idealfall Code im Einzeiler
Hat man weiterhin nur einen einzigen Ausdruck, kann man auch auf zwei Zeilen umbrechen.
Die Verwendung der geschweiften Klammer bietet den Vorteil, dass Schleifen im Code schneller erkannt werden können. Welchen Effekt das aber in Zeiten funktionaler Programmierung mit Aggregate-Operations noch hat, sei mal dahingestellt.
forEach und for-Loop
Alternativ kann forEach auch direkt verwendet
werden, indem wir uns zuerst den Stream für die
Collection verschaffen und über die Aggregate-Operation
forEach(..) dann
angeben, was denn mit jedem einzelnen Element, welches
uns dann über den Stream als Sequenz geliefert wird,
zu tun ist.
collection.stream().forEach(e -> System.out.println(e.toString()));
Collections und Iteratoren
Alle Collections ermöglichen ein Durchlaufen aller Elemente der Gruppe über sogenannte Iteratoren. Iteratoren sind Objekte, welche alle Elemente der Gruppe in irgendeiner Art und Weise quasi durchnumeriert haben und dann über Methoden dieser Iteratoren es ermöglichen, ein Element nach dem anderen zu erhalten.
Iteratoren könnte man vom Prinzip her auch aus Enumeratoren bezeichnen, denn dort, wo man zuvor nur eine von außen zuerst einmal in Bezug auf die interne Ordnung nicht erkennbare Gruppe an Elementen oder quasi Masse von Flöhen hat, stellen Iteratoren sicher, dass wir bei einem Durchlauf alle Elemente geliefert bekommen.
Das Iterator<E> Interface für alle
Collection im Java Collection Framework hat die selbe Struktur:
public interface Iterator<E> {
boolean hasNext(); // true wenn noch ein Element kommen wird
E next(); // Referenz auf nächstes Element
void remove(); // optionales entfernen bzw. ignorieren
}
Der Iterator merkt sich immer die Position in der Sequenz aller Elemente,
die er bereits verarbeitet hat. Will man also vor dem Zugriff auf die
next() Methode prüfen, ob überhaupt noch
ein weiteres Element geliefert werden kann, wird zuvor
hasNext() aufgerufen.
Der einzige verlässliche Ansatz
Wenn man die Gruppe einer Elemente der Collection verlässlich beim Durchlaufen durch das Entferne eines Elements verändern möchte, so ist die Nutzung der remove() Methode des Iterators die EINZIGE VERLÄSSLICHE Variante, das zu tun.
Man ruft zuerst next() auf, um das entsprechende Element zu erhalten, und dann remove(). Die remove() Methode kann also nur das Element entfernen, was zuletzt mit next() geliefert wurde.
Die remove() Methode darf für ein mit next() geliefertes Element auch nur 1x gerufen werden; ein erneuter Aufruf liefert eine Exception.
Wenn bei einem Durchlauf der Elemente der Collection also die remove() Methode aufgerufen wird, so wird nicht nur dieses Elemente bei der weiteren Verarbeitung übersprungen sondern tatsächlich aus der zugrundeliegenden Gruppe aller Elemente der Collection entfernt.
static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); )
if (!checkForRemovement(it.next()))
it.remove();
}
// Oder
static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); ) {
if (!checkForRemovement(it.next())) {
it.remove();
}
}
}
// Damit das funktionieren kann, muss die Klasse
// über eine statische checkForRemovement(..) Methode
// verfügen welche bei einer Prüfung true oder false liefert.
/** statische Methode checkForRemovement(..) dieser Klasse.
static boolean checkForRemovement(Object obj) {
// Beispiel: Entferne Elemente, die "B" oder "D" sind
return !obj.equals("B") && !obj.equals("D");
}
Dieses Beispiel ist «polymorph», des funktioniert also in Java mit jeder Collection.
HINWEIS: Das vollständige Beispiel ist dem Beispiel "Korrektes Entfernen von Elementen mit remove()" zu entnehmen.
Beispiel mit Iterator (Kurzfassung)
Da das Collection Interface das Basis-Interface
für alle anderen Interfaces ist, lässt sich ein Iterator für jedes Collection-Objekt,
egal welche Interfaces die Implementierungsklasse der Collection noch implementiert hat,
über die .iterator() Methode erhalten.
import java.util.*;
public class Main {
public static void main(String[] args) {
Collection<String> collection =
Arrays.asList("Apfel", "Banane", "Kirsche");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
Arrays.asList(..) ist eine Methode der Arrays-Klasse welchen einen variadischen Parameter beinhaltet, welchem die Anzahl der als Parameter übergebenen Werte egal sind.
Den Iterator erhält man über die iterator() Methode der Collection, über die jede Collection seit Java 8 verfügt.
Die Reihenfolge von while, hasNext, next ist die typische Vorgehensweise, wie man mit Iteratoren arbeitet.
Iterator in Langfassung
Für diejenigen, die ohne Arrays.asList() arbeiten möchten:
Wir können auch direkt eine Collection mit dem Typparameter String zur Speicherung aller Elemente realisieren.
import java.util.*;
public class CollectionExample {
public static void main(String[] args) {
// Eine Collection mit drei String-Elementen erstellen
Collection<String> fruits = new ArrayList<>();
fruits.add("Apfel");
fruits.add("Banane");
fruits.add("Kirsche");
// Iterator zum Durchlaufen der Collection verwenden
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println("Frucht: " + fruit);
}
}
}
Die Notation verwendet für ArrayList den Diamond-Operator, mit welchem ab Java 8 die Angabe des Typs für den Typparameter fehlen kann, wenn sich dieser ohnehin aus dem Typ der Collection ergibt, in welcher das Objekt gespeichert werden wird.
ConcurrentModificationException
Beim Entfernen von Elementen aus einer Collection während einer Iteration gibt es ein Problem mit der for-each Schleife (for(String item : collection)) oder forEach(), da sie intern einen Iterator verwenden, aber keine direkte Steuerung über remove() erlauben.
Das führt zu einer ConcurrentModificationException, wenn während der Iteration ein Element entfernt wird.
Man sollte deshalb immer, wenn man bei einem Durchlauf von Elementen einer Collection einzelne Elemente entfernen will, NICHT forEach verwenden sondern über einen Iterator der Collection arbeiten.
Der Iterator ist das einzige Objekt, welches beim Durchlauf über alle Elemente der Collection die Kontrolle über diese Collection hat.
import java.util.*;
public class FilterExample {
/** main-Methode zum Testen */
public static void main(String[] args) {
List<String> items =
new ArrayList<>(
Arrays.asList("A", "B", "C", "D", "E")
);
filter(items);
System.out.println("Gefilterte Liste: " + items);
}
/** statische Methode filter(..) dieser Klasse */
static void filter(Collection<?> c) {
Iterator<?> it = c.iterator();
while (it.hasNext()) {
if (!checkForRemovement(it.next())) {
// Sicheres Entfernen während der Iteration
it.remove();
}
}
}
/** statische Methode checkForRemovement(..) dieser Klasse.
static boolean checkForRemovement(Object obj) {
// Beispiel: Entferne Elemente, die "B" oder "D" sind
return !obj.equals("B") && !obj.equals("D");
}
}
Hinweis: Ein paar der Zeilenumbrüche sind dem Layout dieser Doku geschuldet und hätten vermieden werden können.
Collection Interface Bulk Operations
Die sogenannten Collection Interface Bulk Operations führen Operationen an der gesamten Collection durch. Man kann also als Entwickler diese Operationen dann verwenden, wenn man tatsächlich alle Elemente verarbeiten möchte.
Wie performant und effizient diese Bulk-Operations dann allerdings sind, steht und fällt mit der Problemstellung. Die neuen stream()- und parallelStream()-basierten Varianten können effizienter sein.
returns true if the target Collection contains all of the elements in the specified Collection.
Fügt alle Elemente der als Parameter über gebenen Collection zur target-Collection hinzu.
targetCollection.addAll(parameterCollection);
Entfernt alle Elemente der Collection welche in der als Parameter übergebenen Collection zu finden sind. Damit ist es also möglich, mehrere Elemente auf einmal zu entfernen.
Entfernt alle Elemente von der target-Collectoin, deren Elemente sich nicht in der parameter-Collection befinden.
Die Collection umfasst also am Ende nur noch die Elemente, die als Referenz über die Collection im übergebenen Parameter enthalten waren. Wir bezeichnen das mitunter als "Green-Listing"-Prinzip.
Und weg damit ...
Entfernt alle Elemente der Collection.
Was dann mit diesen passiert im Arbeitsspeicher ist eine Frage der Garbage-Collection der Java Runtime.
Will man alle Elemente, die als Objekt in der Collection enthalten, über removeAll löschen, so ist folgender Ansatz empfehlen:
// Alle Person-Elemente einer Collection
// entfernen, welche dem Objekt in e
// entsprechen.
Collection<Person> c;
Person e;
c.removeAll(Collections.singleton(e));
// Alle null-Werte in der Collection entfernen.
c.removeAll(Collections.singleton(null));
// Prinzip, wie man immutable Sets erzeugen kann
import java.util.Collections;
import java.util.Set;
public class FactoryOfImmutableSets {
// Variante 1
public static <T> Set<T> createImmutableSet(T element) {
return Collections.unmodifiableSet(Collections.singleton(element));
}
// Variante 2
public static <T> Set<T> createImmutableSet(Set<T> elements) {
return Collections.unmodifiableSet(elements);
}
public static void main(String[] args) {
Set<String> immutableSet1 = FactoryOfImmutableSets.createImmutableSet("Hallo");
System.out.println("Immutable Set 1: " + immutableSet1);
Set<Integer> immutableSet2 = FactoryOfImmutableSets.createImmutableSet(Set.of(1, 2, 3));
System.out.println("Immutable Set 2: " + immutableSet2);
}
}
Die statische Methode Collections.singleton(e) liefert ein unveränderliches Set,
engl. immutable Set, welches als einziges Objekt das als Parameter e übergebene
Objekt beinhaltet.
Collection Interface Array Operations
Mit Hilfe von Collection Interface Array Operations ist es möglich, Collections in Arrays und Arrays in Collections umwandeln zu können. Eine Vielzahl alter Packages arbeitet mit Arrays und noch nicht mit Collections.
Die Elemente einer Collection als Array liefern
Kurz und schmwerzlos:
Collection<Object> c = Datenlieferant.getCollection();
Object[] a = c.toArray();
Der Array hat eine Größe und liefert damit den length Wert welcher der Anzahl der Elemente in der Collection c entspricht.
Die Elemente einer Collection als Array liefern
Wenn alle Elemente der Collection den selben Typ haben und man dem Array auch gleich den richtigen Typ geben kann, so übergibt man der toArray Methode einen Array von Objekten dieses Typs. Da man diese Objekte zu Beginn nicht hat, ist die Anzahl 0.
import java.util.*;
public class Main {
public static void main(String[] args) {
Collection<String> c =
Arrays.asList("Apfel", "Banane", "Kirsche");
String[] a = c.toArray(new String[0]);
// Ausgabe: [Apfel, Banane, Kirsche]
System.out.println(Arrays.toString(a));
}
}
Die Methode toArray(T[] a) erwartet ein Array als Parameter, das bestimmt, welchen Typ das Zielarray haben soll. Das Argument new String[0] bedeutet, dass ein leeres Array übergeben wird. Java wird dann automatisch ein neues Array der richtigen Größe erstellen und die Elemente aus c hinein kopieren.
Zusammenfassung
Collection | Zusammenfassung
Links
Quellen, Notes, Tags
UIO3 Es ist einfacher als Du denkst.
Stelle noch heute Deine Anfrage.

