Learnitweb

Protocol Buffers Collections

So far, we have modeled single objects in Protocol Buffers, such as a Person, Address, or Student. However, real-world systems often need to handle collections of items, such as a list of books in a library, a list of orders in an e-commerce system, or a list of messages in a chat application.

In this tutorial, we learn how to model collections in Protobuf using repeated fields, which allow a message to contain multiple values of the same type.

1. Why Do We Need Collections in Protobuf?

Sometimes a single value is not enough.

  • A library contains many books, not just one.
  • A student can have multiple phone numbers.
  • An order can contain multiple products.

So we need a way to represent a list (or collection) of items in a message.

In Protobuf, this is done using the repeated keyword.

2. Defining the .proto File

Create a file collection.proto. Use Proto3 syntax and standard options

collection.proto

syntax = "proto3";

package example03;

option java_multiple_files = true;
option java_package = "com.learnitweb.example03";
option java_outer_classname = "CollectionProto";

// Book message
message Book {
  string title = 1;
  string author = 2;
  int32 publication_year = 3;
}

// Library message
message Library {
  string name = 1;
  repeated Book books = 2;
}

3. Understanding repeated

The key line is:

repeated Book books = 2;

This means:

  • books is a collection (list) of Book messages.
  • You can add zero, one, or many Book objects.
  • Internally, this behaves like a list.

You can think of it as:

List<Book> books;

So the Library message contains a collection of Book objects.

4. Compile the Protobuf

After creating the .proto file, run:

mvn clean compile

This generates the Java classes from the .proto definition.

5. Java Example: Creating Books and a Library

package org.learnitweb;

import com.learnitweb.example03.Book;
import com.learnitweb.example03.Library;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {

        // Book 1
        Book book1 = Book.newBuilder()
                .setTitle("Harry Potter Part 1")
                .setAuthor("J.K. Rowling")
                .setPublicationYear(1997)
                .build();

        // Book 2
        Book book2 = Book.newBuilder()
                .setTitle("Harry Potter Part 2")
                .setAuthor("J.K. Rowling")
                .setPublicationYear(1998)
                .build();

        // Book 3
        Book book3 = Book.newBuilder()
                .setTitle("Harry Potter Part 3")
                .setAuthor("J.K. Rowling")
                .setPublicationYear(1999)
                .build();

        // Library
        Library library = Library.newBuilder()
                .setName("Fantasy Library")
                .addBooks(book1)
                .addBooks(book2)
                .addBooks(book3)
                .build();

        System.out.println(library);
    }
}

This prints the library with all its books.

Output

name: "Fantasy Library"
books {
  title: "Harry Potter Part 1"
  author: "J.K. Rowling"
  publication_year: 1997
}
books {
  title: "Harry Potter Part 2"
  author: "J.K. Rowling"
  publication_year: 1998
}
books {
  title: "Harry Potter Part 3"
  author: "J.K. Rowling"
  publication_year: 1999
}

6. Adding Items One by One vs Bulk Add

Adding One by One

.addBooks(book1)
.addBooks(book2)
.addBooks(book3)

This works well for a small number of items.

But what if you have hundreds of books?

Adding a List (Bulk Add)

If you already have a list:

List<Book> bookList = List.of(book1, book2, book3);

Library library = Library.newBuilder()
        .setName("Fantasy Library")
        .addAllBooks(bookList)
        .build();

This is much cleaner and more scalable.

So:

  • Use addBooks() for single items.
  • Use addAllBooks() for collections.

7. Getter Methods for Repeated Fields

Repeated fields generate slightly different getters compared to normal fields.

Assume:

repeated Book books = 2;

7.1 Get Book by Index

library.getBooks(0);

This returns the book at index 0.

7.2 Get Count

library.getBooksCount();

This returns how many books exist.

7.3 Get All Books as List

library.getBooksList();

This returns:

List<Book>

This is how you access all books.

8. Why Not Just getBooks()?

Many developers expect:

getBooks();

But Protobuf does not generate this for repeated fields.

Instead:

  • getBooks(index)
  • getBooksCount()
  • getBooksList()

This naming is intentional and consistent in Protobuf.

9. Field Name Matters

Method names are derived from the field name.

If your field is:

repeated Book items = 2;

You would get:

  • getItems(index)
  • getItemsCount()
  • getItemsList()

So choose field names carefully.