Introduction

The Google Guava Lists Utility class has static utility methods for working with a Java List. Let us learn about some useful methods from it.

Lists newArrayList

There are several overloaded newArrayList method in the Google Guava Lists class. Actually, several of them (if not all) aren’t that useful with Java 8+ or 9+. There are options and methods in JDK which we can use instead.

Example: List.of() methods added in Java 9.

The newArrayList method creates and returns a mutable Java ArrayList. Lists.newArrayList() creates an empty list.

List<Integer> list1 = Lists.newArrayList();
System.out.println(list1); //[]

We can pass a var-args argument and the array list it returns will have those elements.

list1 = Lists.newArrayList(1, 2, 3);
System.out.println(list1);

newArrayList with an Iterable and Iterator

We can build an ArrayList from an existing Iterable or Iterator.

Iterable<Integer> iterable = list1;
List<Integer> list2 = Lists.newArrayList(iterable);
System.out.println(list2); //[1, 2, 3]

Iterator<Integer> iterator = list1.iterator();
list2 = Lists.newArrayList(iterator);
System.out.println(list2); //[1, 2, 3]

Lists newArrayListWithCapacity

The newArrayListWithCapacity method creates an ArrayList with the specified capacity.

List<Integer> listWithCapacity = Lists.newArrayListWithCapacity(5);

We could instead directly use the new ArrayList<>(capacity) instead.

Lists newLinkedList

Even these methods aren’t that useful.

The newLinkedList method creates and returns a LinkedList instance. We can create an empty LinkedList as:

List<Integer> list = Lists.newLinkedList();
System.out.println(list); // []

Or we can pass an Iterable and create a LinkedList from it.

List<Integer> list = List.of(1, 2);
List<Integer> list2 = Lists.newLinkedList(list);
System.out.println(list2); //[1, 2]

Lists - asList

There are two overloaded asList methods. The asList method returns an unmodifiable list with the passed elements. It takes one element and the rest of the elements as an array. Its signature is:

public static <E> List<E> asList(@Nullable E first, E[] rest) 

Hence, this is useful to call this from a method taking a varargs argument (will see an example). It doesn’t copy the passed array and hence any changes to it will be reflected in the list returned.

The second overloaded asList takes two elements in addition to the array of elements.

public static <E> List<E> asList(@Nullable E first, @Nullable E second, E[] rest) 

Google Guava Lists asList example

Let us say we have a method taking one mandatory element and a varargs of elements and want to build a list with these elements and return it. We can use Lists#asList method here.

private static List<Integer> asListWithOneMandatory(Integer first, Integer... rest) {
    return Lists.asList(first, rest);
}

For the other asList method, we have a method taking two mandatory elements and a third varargs parameter.

private static List<Integer> asListWithTwoMandatory(Integer first, Integer second, Integer... rest) {
    return Lists.asList(first, second, rest);
}
System.out.println(asListWithOneMandatory(1)); //[1]
System.out.println(asListWithOneMandatory(1, 2, 3, 4, 5)); //[1, 2, 3, 4, 5]

Here we call the asListWithOneMandatory method passing either one element or many elements. We get back a list of passed elements.

Similarly, for the second case, we have,

System.out.println(asListWithTwoMandatory(1, 2)); //[1, 2]
System.out.println(asListWithTwoMandatory(1, 2, 3, 4, 5)); //[1, 2, 3, 4, 5] 

Lists charactersOf

The charactersOf method takes a String and returns an ImmutableList<Character> which has the individual characters from the passed string.

List<Character> characters = Lists.charactersOf("abcd");
System.out.println(characters); //[a, b, c, d]

There is another overloaded method which takes a CharSequence rather than a String. It returns a List<Character> and the method is marked @Beta (experimental).

CharSequence charSequence = "abcd";
characters = Lists.charactersOf(charSequence);
System.out.println(characters); //[a, b, c, d]

Lists reverse

The Lists#reverse takes a list and returns a new list with the elements reversed.

List<Integer> list = List.of(1, 2, 3, 4, 5);
List<Integer> reversedList = Lists.reverse(list);
System.out.println(reversedList); //[5, 4, 3, 2, 1]

Note that the returned list is backed by the original list and hence any changes in either list will be reflected in the other (subject to the mutability of the source list). Shown below an example of this when using a mutable list.

//add to reversed list
reversedList.add(6);
System.out.println(list); //[6, 1, 2, 3, 4, 5]
System.out.println(reversedList); //[5, 4, 3, 2, 1, 6]
System.out.println();

//add to original list
list.add(7);
System.out.println(list); //[6, 1, 2, 3, 4, 5, 7]
System.out.println(reversedList); //[7, 5, 4, 3, 2, 1, 6]

Lists transform method

It takes a list and a function and applies the function to each element of the list. In Java 8+, it is recommended to use the Stream’s map operation.

As an example, let us convert a list of integers to strings.

List<Integer> ints = List.of(1, 2, 3, 4, 5);
List<String> strings = Lists.transform(
        ints,
        String::valueOf);
System.out.println(strings); //[1, 2, 3, 4, 5]

In this example, we can write the method reference String::valueOf as a lambda expression as i -> String.valueOf(i).

Partitioning a list using Google Guava Lists#partition

We use the Lists#partition method to partition a list into sublists. It takes a list (List<T>)and a size parameter and returns a List<List<T>> which is a consecutive sublists of the passed list. The size parameter is the desired size of each sublist. It uses Java List#subList method internally.

List<Integer> ints = List.of(1, 2, 3, 4, 5, 6);
List<List<Integer>> partitionedList = Lists.partition(ints, 3);
System.out.println(partitionedList);

Prints,

[[1, 2, 3], [4, 5, 6]]

In the above example, we have a list of 6 items. Since we ask it to partition into lists where each sublist is of size 3, it returns two sublists (each having three elements).

Each of the lists returned will have the same size, but the last one may be smaller, as shown in the below example.

List<Integer> ints = List.of(1, 2, 3, 4, 5, 6, 7);
partitionedList = Lists.partition(ints, 3);
System.out.println(partitionedList); //[[1, 2, 3], [4, 5, 6], [7]]

The outer list it returns is unmodifiable and note that the sublist it returns is only a view of the original list (no copies are made). As per the Java List#subList, if any structural modification is made to the backing list, then the semantics of the sublist is undefined.

Google Guava Lists cartesian product

The cartesianProduct method takes a var-args of list and returns every possible list that we can form by choosing one element from each of the given lists in order. Let us look at an example.

List<List<Integer>> cartesianProduct = Lists.cartesianProduct(
        List.of(1, 2),
        List.of(3, 4)
);
System.out.println(cartesianProduct);

This outputs,

[[1, 3], [1, 4], [2, 3], [2, 4]]

In the above example, we passed two lists. For each element in the outer list, it picks or matches each element from the inner list (second list). The result is we have a List<List<Integer>> with four outer elements.

Another example,

List<List<Integer>> cartesianProduct = Lists.cartesianProduct(
        List.of(1, 2),
        List.of(3, 4, 5)
);
System.out.println(cartesianProduct); //[[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]]

If we have more than two lists,

List<List<Integer>> cartesianProduct = Lists.cartesianProduct(
        List.of(1, 2),
        List.of(3, 4, 5),
        List.of(6, 7)
);
System.out.println(cartesianProduct);

It prints (showing one element in each line for readability),

[
  [1, 3, 6]
  [1, 3, 7]
  [1, 4, 6]
  [1, 4, 7]
  [1, 5, 6]
  [1, 5, 7]
  [2, 3, 6]
  [2, 3, 7]
  [2, 4, 6]
  [2, 4, 7]
  [2, 5, 6]
  [2, 5, 7]
]

cartesianProduct with a list of lists

There is an overloaded cartesianProduct method which takes a list of list. The method signature is:

public static <B> List<List<B>> cartesianProduct(List<? extends List<? extends B>> lists)
List<List<Integer>> input = List.of(
        List.of(1, 2),
        List.of(3, 4, 5));
List<List<Integer>> cartesianProduct = Lists.cartesianProduct(input);
System.out.println(cartesianProduct); //[[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]]

cartesianProduct with empty lists

Note that, if any one of the lists is empty, the cartesian product is empty as well.

List<List<Integer>> input = List.of(
        List.of(1, 2),
        List.of(3, 4, 5),
        List.of());
List<List<Integer>> cartesianProduct = Lists.cartesianProduct(input);
System.out.println(cartesianProduct); //[]

This is true even if we pass only the empty inner list.

List<List<Integer>> input = List.of(
        List.of());
List<List<Integer>> cartesianProduct = Lists.cartesianProduct(input);
System.out.println(cartesianProduct);//[]

If we pass en empty outer list (no lists), then the cartesian product has one element (an empty list).

List<List<Integer>> input = List.of();
List<List<Integer>> cartesianProduct = Lists.cartesianProduct(input);
System.out.println(cartesianProduct); //[[]]

Conclusion

In this post, we learnt about the Google Guava Lists Utility class. Check out the other google-guava posts here as well.