This tutorial explains the usage of the Java collections, e.g. Lists, ArrayLists and Maps with Java.
1. Java Collections
The Java language supports arrays to store several objects.
An array is initialized with a predefined size during instantiation.
To support more flexible data structures the core Java library provides the collection
framework.
A collection is a data structure which contains and processes a set of data.
The data stored in the collection is encapsulated and the access to the data is only possible via predefined methods.
For example the developer can add elements to an collection via a method.
Collections use internally arrays for there storage but hide the complexity of managing the dynamic size from the developer.
For example if your application saves data in an object of type People
,
you can store several People
objects in a collection.
1.1. Important default implementations
Typical collections are: stacks, queues, deques, lists and trees.
Java typically provides an interface, like
List
and one or several implementations for this interface, e.g.,
the
ArrayList
class and the
LinkedList
are implementations of the
List
interface.
1.2. Type information with generics
A class or interface whose declaration has one or more type parameters is a generic class or interface.
For example
List
defines one type parameter
List<E>
.
Java collections should get parameterized with an type declaration. This enables the Java compiler to check if you try to use your collection with the correct type of objects. Generics allow a type or method to operate on objects of various types while providing compile-time type safety. Before generics you had to cast every object you read from a collection and if you inserted an object of the wrong type into a collection you would create a runtime exception.
1.3. Collections and lambdas
The collection library support lambdas expressions. Operations on collections have been largely simplified with this.
The following code shows an example how to create a Collection of
type
List
which is parameterized with <String>
to indicate to the Java compiler that only Strings are allowed in
this
list. Is uses a method reference and the
foreach
loop from Java 8.
package collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MyArrayList {
public static void main(String[] args) {
// create a list using the
List<String> list = Arrays.asList("Lars", "Simon");
// alternatively
List<String> anotherList = new ArrayList<>();
anotherList.add("Lars");
anotherList.add("Simon");
// print each element to the console using method references
list.forEach(System.out::println);
anotherList.forEach(System.out::println);
}
}
If you try to put an object into this list which is not an String, you would receive a compiler error.
2. List implementations
2.1. The List interface
The
List
interface is the base
interface for collections which allows to store
objects in a resizable
container.
2.2. ArrayList and LinkedList as implementations
ArrayList
is an implementation of this interface and allow that elements are
dynamically added and removed from the list. If more elements are
added to
ArrayList than its initial size, its size is increased
dynamically.
The elements in an
ArrayList
can be
accessed directly and efficiently by using the
get()
and
set()
methods, since
ArrayList
is implemented based on an array.
ArrayList
is typically used in implementations as implementation class for the
List
interface.
LinkedList
is implemented as a double linked list. Its
performance on
add()
and
remove()
is better than the performance of Arraylist. The
get()
and
set()
methods have worse performance than the ArrayList, as the
LinkedList
does not provide direct access to its members.
The following code demonstrates the usage of
List
and
ArrayList
.
package com.vogella.java.collections.list;
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
// use type inference for ArrayList
List<Integer> list = Arrays.asList(3,2,1,4,5,6,6);
// alternative you can declare the list via:
// List<Integer> list = new ArrayList<>();
// and use list.add(element); to add elements
for (Integer integer : list) {
System.out.println(integer);
}
}
}
2.3. Sorting lists
You can sort lists using their natural order of via lambdas for defining the Comparator.compare()
.
Typically in Java 8 you use a lambda expression or a method reference for the definion of the compare
method.
package com.vogella.java.collections.list;
import java.util.ArrayList;
import java.util.List;
public class ListSorter {
public static void main(String[] args) {
System.out.println("Sorting with natural order");
List<String> l1 = createList();
l1.sort(null);
l1.forEach(System.out::println);
System.out.println("Sorting with a lamba expression for the comparison");
List<String> l2 = createList();
l2.sort((s1, s2) -> s1.compareToIgnoreCase(s2)); // sort ignoring case
l2.forEach(System.out::println);
System.out.println("Sorting with a method references");
List<String> l3 = createList();
l3.sort(String::compareToIgnoreCase);
l3.forEach(System.out::println);
}
private static List<String> createList() {
return Arrays.asList("iPhone", "Ubuntu", "Android", "Mac OS X");
}
}
2.4. Remove list members based on condition
The
removeIf
method allows to remove list items based on a condition.
package com.vogella.java.collections.list;
import java.util.ArrayList;
import java.util.List;
public class RemoveIfList {
public static void main(String[] args) {
System.out.println("Demonstration of removeIf");
List<String> l1 = createList();
// remove all items which contains an "x"
l1.removeIf(s-> s.toLowerCase().contains("x"));
l1.forEach(s->System.out.println(s));
}
private static List<String> createList() {
List<String> anotherList = new ArrayList<>();
anotherList.addAll(Arrays.asList("iPhone", "Ubuntu", "Android",
"Mac OS X"));
return anotherList;
}
}
3. Using Maps
3.1. Map and HashMap
The Map
interface defines an object that maps keys to values.
A map cannot contain duplicate keys; each key can map to at most one value.
The HashMap
class is an efficient implementation of the Map
interface.
3.2. Example Program
To see the following code snippets in action, insert them into the main method where put test code here is stated.
package com.vogella.java.collections.map;
import java.util.HashMap;
import java.util.Map;
public class MapTester {
public static void main(String[] args) {
// put test code here
}
}
3.3. Initialize a HashMap in Java
The following code demonstrates how to initialize a HashMap
in Java.
3.3.1. Initialize an Empty Map
In the following code, an empty map with keys and values of type String
is initialized.
Map<String, String> map = Map.of();
3.3.2. Initialize an Unmodifiable Map with Map.of for Small Maps
In the following example, a map with several entries is initialized. This factory method supports a maximum of 10 key-value pairs and requires at least Java 9.
Map<String, String> map = Map.of("key1", "value1", "key2", "value2");
3.3.3. Initialize an Unmodifiable Map via the Map.ofEntries()
Map<String, String> map = Map.ofEntries(
Map.entry("key1", "value1"),
Map.entry("key2", "value2"),
// more entries can be added here
Map.entry("key100", "value100")
);
3.3.4. Initialize a Map via the New Operator
The following code creates a map using the new
operator and adds multiple entries to it via the put
method.
Map<String, String> map = new HashMap<>();
map.put("Android", "Mobile");
map.put("Eclipse IDE", "Java");
map.put("Eclipse RCP", "Java");
map.put("Git", "Version control system");
3.4. Remove an Entry from a Map
You can remove an entry from a map using the remove
method.
Map<String, String> map = Map.of("Android", "Mobile OS", "Flutter", "Development environment");
map.remove("Android");
3.5. Process the Map
To process every element in a map, you can use the forEach
method, which takes a lambda as a parameter.
map.forEach((k, v) -> System.out.printf("%s %s%n", k, v));
3.6. Convert Keys in a Map to an Array or a List
You can convert the keys or values to an array or a list. The following code demonstrates this process.
Map<String, String> map = new HashMap<>();
map.put("Android", "Mobile");
map.put("Eclipse IDE", "Java");
map.put("Eclipse RCP", "Java");
map.put("Git", "Version control system");
// Convert keys to array
String[] keys = map.keySet().toArray(new String[0]);
for (String key : keys) {
System.out.println(key);
}
// Convert keys to list
List<String> list = new ArrayList<>(map.keySet());
for (String key : list) {
System.out.println(key);
}
3.7. Getting the Current Value or a Default for a Map
You can use the getOrDefault()
method to return the value for a key or a default value if the key is not present in the map.
Map<String, Integer> map = new HashMap<>();
map.put("Android", 1 + map.getOrDefault("Android", 0));
// Write to command line
map.forEach((k, v) -> System.out.printf("%s %s%n", k, v));
3.8. Compute If Absent
The computeIfAbsent
method calculates and adds an entry to the map if it is not already present.
Map<String, Integer> map = new HashMap<>();
Integer calculatedValue = map.computeIfAbsent("Java", it -> 0);
System.out.println(calculatedValue);
map.keySet().forEach(key -> System.out.println(key + " " + map.get(key)));
4. Sorting Maps in Java
Sorting maps in Java can be achieved in several ways, depending on the requirements for ordering keys or values. This chapter explores the methods to sort maps, focusing on the TreeMap
for key sorting and using streams for sorting by values.
4.1. Sorting by Keys
The simplest way to sort a map by keys is to use TreeMap
, which maintains the natural ordering of its keys or can be constructed with a custom comparator.
4.1.1. Using TreeMap
A TreeMap
automatically sorts the keys in their natural order or according to a specified comparator.
import java.util.Map;
import java.util.TreeMap;
public class SortMapByKeys {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Display the sorted map
map.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
4.2. Sorting by Values
To sort a map by its values, you can use Java Streams to create a sorted representation of the entries.
4.2.1. Example of Sorting by Values
Here’s how to sort a HashMap
by values using streams:
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class SortMapByValues {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Sort by values
Map<String, Integer> sortedByValues = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new // Maintain insertion order
));
// Display the sorted map
sortedByValues.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
4.3. Custom Sorting
You can also implement custom sorting logic using comparators. For instance, to sort a map by values in descending order:
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class CustomSortMapByValues {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Banana", 3);
map.put("Apple", 5);
map.put("Orange", 2);
// Sort by values in descending order
Map<String, Integer> sortedByValuesDescending = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue((v1, v2) -> v2.compareTo(v1))) // Custom comparator
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new // Maintain insertion order
));
// Display the sorted map
sortedByValuesDescending.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
4.4. Conclusion
Sorting maps can be easily accomplished using TreeMap
for key-based sorting or Java Streams for value-based sorting.
By employing custom comparators, you can tailor the sorting behavior to suit your application’s requirements.
Understanding these methods enhances your ability to manipulate and present data effectively in Java.
5. Useful collection methods
The
java.util.Collections
class provides useful functionalities for working with collections.
Method | Description |
---|---|
Collections.copy(list, list) |
Copy a collection to another |
Collections.reverse(list) |
Reverse the order of the list |
Collections.shuffle(list) |
Shuffle the list |
Collections.sort(list) |
Sort the list |
6. Using Collections.sort and Comparator in Java
Sorting a collection in Java is easy, just use the
Collections.sort(Collection)
to sort your values. The following code shows an example for this.
package collections;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Simple {
public static void main(String[] args) {
// create a new ArrayList with the Arrays.asList helper method
List<Integer> list = Arrays.asList(5,4,3,7,2,1);
// sort it
Collections.sort(list);
// print each element to the console
list.forEach(System.out::println);
}
}
This is possible because Integer implements the
Comparable
interface.
This interface defines the method compare which performs pairwise comparison of the elements and returns -1 if the element is smaller than the compared element, 0 if it is equal and 1 if it is larger.
If what to sort differently you can define your custom implementation
based on the
Comparator
interface via a lambda expression.
package collections;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ListCustomSorterExample {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(5,4,3,7,2,1);
// custom comparator
Collections.sort(list, (o1, o2) -> (o1>o2 ? -1 : (o1==o2 ? 0 : 1)));
// alternative can could reuse the integer comparator
// Collections.sort(list, Integer::);
list.forEach(System.out::println);
}
}
You can sort by any
any attribute or
even
a combination of attributes. For
example if you
have objects of
type
Person
with the attributes called
income
and
dataOfBirth
you could define different implementations of
Comparator
and sort the objects according to your needs.
7. Exercise: Use Java Collections
Create a new Java project called
com.vogella.java.collections
.
Also add a package with the same name.
Create a Java class called
Server
with one String attribute called
url
.
package com.vogella.java.collections;
public class Server {
private String url;
}
Create getter and setter methods for this attribute using code generation capabilities of Eclipse. For this select
from the Eclipse menu.Create via Eclipse a constructor which gets a url as parameter. For this select
from the Eclipse menu.Type
main
in the class body and use code completion (
Ctrl+Space
) to generate a
main
method.
In your
main
method create a
List
of type
ArrayList
and add 3 objects of type
Server
objects to this list.
public static void main(String[] args) {
List<Server> list = new ArrayList<Server>();
list.add(new Server("https://www.vogella.com/"));
list.add(new Server("http://www.google.com"));
list.add(new Server("http://www.heise.de"));
}
Use code completion to create a
foreach
loop and write the
toString
method to the console. Use code completion based on
syso
for that.
Run your program.
Use Eclipse to create a
toString
method based on the
url
parameter and re-run your program again.
8. Links and Literature
Nothing listed.
8.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting