Home Tutorials Training Consulting Books Company Contact us


Get more...

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.

Table 1. 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 Source  Generate Getters and Setters from the Eclipse menu.

Create via Eclipse a constructor which gets a url as parameter. For this select Source  Generate Constructor using Fields…​ from the Eclipse menu.

Type main in the class body and use code completion ( Ctrl+Space ) to generate a main method.

Code completion

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