Skip to content

Builders

Vector.Builder

A mutable, single-use accumulator for a Vector. Use it when you build a Vector in a loop: each append on a Vector copies part of it, while the builder writes each element once.

var builder = Vector.<String>newBuilder(); // Vector.Builder<String>
for (String word : "the quick brown fox".split(" ")) {
    builder.add(word.toUpperCase());
}
var words = builder.result(); // Vector<String>
// Vector(THE, QUICK, BROWN, FOX)
Member Does
Vector.newBuilder() an empty builder
add(a) appends one element
addAll(iterable) appends every element
size() the number of elements added so far
result() the Vector; afterwards every method throws IllegalStateException

A builder is not thread-safe and not reusable: after result(), create a new one. Adding null throws a NullPointerException.

newBuilder(sizeHint) takes the expected size. The hint is checked but currently changes nothing: the builder costs the same as newBuilder().

var both   = Vector.<Integer>newBuilder(8);  // Vector.Builder<Integer>
both.addAll(Vector.of(1, 2, 3)).add(4);
var added  = both.size();
var result = both.result();                  // Vector<Integer>
// added is 4, result is Vector(1, 2, 3, 4)

Vector's own bulk operations, such as ofAll, flatMap or collect, already use a builder.

Map and set builders

HashMap, HashSet, TreeMap and TreeSet have a builder of the same shape. Maps use put and putAll instead of add and addAll.

var counts = HashMap.<String, Integer>newBuilder(); // HashMap.Builder<String, Integer>
for (var word : "to be or not to be".split(" ")) {
    counts.put(word, word.length());
}
var lengths = counts.result(); // HashMap<String, Integer>
// HashMap((to, 2), (be, 2), (or, 2), (not, 3)), in some order

When two entries have equal keys, the one put last wins, as with successive put calls. A HashSet does the opposite: of equal elements, it keeps the one added first, as add does.

A HashMap or HashSet builder changes its own nodes in place. A persistent put would copy the path from the root each time. Give an empty builder an existing map with putAll, and it starts from that map without copying it. That map never changes.

A TreeMap or TreeSet builder collects the elements, then sorts them once in result() and builds a balanced tree in one pass. Input that is already sorted costs a single pass. newBuilder(comparator) sets the order; the natural order is the default.

var sorted = TreeSet.newBuilder(java.util.Comparator.<String>reverseOrder())
    .addAll(List.of("pear", "apple", "fig"))
    .result(); // TreeSet<String>
// TreeSet(pear, fig, apple)

The comparator runs in size() and result(), not in add. On a TreeMap or TreeSet builder, size() sorts what was added since the last call.

The ofAll and ofEntries factories of these four types, and their collectors, use the builders.

Insertion-ordered builders

LinkedHashMap and LinkedHashSet have a builder of the same shape. It gives what calling put or add one by one gives, without making a new map at each step.

var firstSeen = LinkedHashSet.<String>newBuilder()
    .addAll(List.of("b", "a", "b", "c"))
    .result(); // LinkedHashSet<String>
var latest = LinkedHashMap.<String, Integer>newBuilder()
    .put("b", 1).put("a", 2).put("b", 3)
    .result(); // LinkedHashMap<String, Integer>
// LinkedHashSet(b, a, c), LinkedHashMap((b, 3), (a, 2))

A key put twice keeps its first position and takes the last key object and value. An element added twice keeps its first position and its first object.

Give an empty builder an existing LinkedHashMap with putAll, or a LinkedHashSet with addAll, and result() returns it as it is if nothing else is added. That map or set never changes.

LinkedHashMap.ofEntries, the ofAll factories of both types and their collectors use the builders.

List.Builder

A List is built from its last element to its first. The builder keeps the elements in an array, then result() makes one cell per element, from the end. Prepending in a loop and reversing makes every cell twice.

var builder = List.<Integer>newBuilder(); // List.Builder<Integer>
for (int i = 1; i <= 3; i++) {
    builder.add(i * i);
}
var squares = builder.result(); // List<Integer>
// List(1, 4, 9)

A List given to addAll becomes the end of the result as it is, when nothing is added after it. Its cells are shared, not copied.

var tail  = List.of(8, 9);
var whole = List.<Integer>newBuilder().add(7).addAll(tail).result(); // List<Integer>
// List(7, 8, 9); whole.tail() is tail itself

List.ofAll of anything but a java.util.List, List.flatten and the collector use the builder.

Collectors

Every collection has a collector() for java.util.stream.Stream.collect. The collectors of Vector, List, HashMap, HashSet, TreeMap, TreeSet, LinkedHashMap and LinkedHashSet use their builder.

var lengths = java.util.stream.Stream.of("a", "bb", "ccc").map(String::length)
    .collect(Vector.collector()); // Vector<Integer>
var sorted = java.util.stream.Stream.of("b", "a", "b").collect(TreeSet.collector()); // TreeSet<String>
// Vector(1, 2, 3), TreeSet(a, b)

Queue has no builder yet. Its ofAll factories take any Iterable or java.util.stream.Stream.