Java Protobuf Maps Example
Protocol Buffers (Protobuf) is a language-neutral, platform-neutral extensible mechanism for serializing structured data. Let us delve into understanding how Java Protobuf maps work and how they enable efficient key-value pair representation within message definitions.
1. Understanding Maps in Protobuf
Protobuf (Protocol Buffers), developed by Google, supports the use of maps to represent key-value pairs directly within message definitions. This makes it easier to work with associative arrays or dictionaries when designing your schema.
A map field in a Protobuf message is declared using the following syntax:
map<key_type, value_type> map_field = N;
The key_type must be a scalar type such as int32, int64, uint32, uint64, bool, or string. Notably, floating-point numbers (float, double) and composite types (such as other messages or enums) are not permitted as map keys due to serialization concerns. The value_type can be any valid Protobuf type, including scalar types (like int32, string, etc.) and complex types (like nested message types). This allows you to structure your data more richly when needed.
2. Implementing Maps in Protobuf
Let us delve into understanding how Java Protobuf maps can be implemented to manage structured key-value data efficiently.
2.1 Define the Protobuf File
The following is a simple .proto file that defines an AddressBook message with a map from email strings to `Person` messages.
// address_book.proto
syntax = "proto3";
option java_package = "com.example.protobuf";
option java_outer_classname = "AddressBookProto";
// A map field named 'contacts' mapping email (string) to Person
message AddressBook {
map<string, Person> contacts = 1;
}
message Person {
string name = 1;
int32 age = 2;
}
In the definition above, map<string, Person> contacts = 1; creates a field that maps email IDs to Person entries, and Person is a nested message that stores name and age details.
2.1.1 Integrating Protobuf with Maven
To compile .proto files automatically during the Maven build, you can use the protobuf-maven-plugin along with the os-maven-plugin to handle platform-specific settings.
2.1.1.1 Add dependencies to your pom.xml
<project>
...
<dependencies>
<!-- Protobuf runtime for Java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Plugin to compile .proto files -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.1:exe:${os.detected.classifier}</protocArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Detect OS for protoc binary -->
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
<executions>
<execution>
<goals>
<goal>detect</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2.1.1.2 Organize your .proto files
Put your proto files in the following standard Maven path:
src/main/proto/address_book.proto
2.1.1.3 Build the project
To generate the Java code from proto files, run:
mvn clean install
This compiles the .proto files and places the generated code in:
target/generated-sources/protobuf/java
These files are added automatically to your build classpath.
2.1.1.4 Step 4: Use the Generated Classes
Now you can use the generated classes like AddressBookProto.AddressBook and AddressBookProto.Person in your Java application, as shown earlier in section 2.3.
2.2 Compile the Protobuf File
To generate Java classes from the .proto file, use the Protocol Buffers compiler (protoc):
protoc --java_out=. address_book.proto
This generates AddressBookProto.java inside the specified package structure, containing classes for AddressBook and Person.
2.3 Java Code to Use the Map
Here’s how you can create and use the contacts map field in Java:
import com.example.protobuf.AddressBookProto.AddressBook;
import com.example.protobuf.AddressBookProto.Person;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.Map;
public class ProtobufMapDemo {
public static void main(String[] args) throws Exception {
// Create person entries
Person alice = Person.newBuilder().setName("Alice").setAge(30).build();
Person bob = Person.newBuilder().setName("Bob").setAge(25).build();
// Create address book with a map
AddressBook.Builder bookBuilder = AddressBook.newBuilder();
bookBuilder.putContacts("alice@example.com", alice);
bookBuilder.putContacts("bob@example.com", bob);
AddressBook book = bookBuilder.build();
// Serialize to binary file
try (FileOutputStream fos = new FileOutputStream("addressbook.bin")) {
book.writeTo(fos);
}
// Deserialize from binary file
AddressBook readBook;
try (FileInputStream fis = new FileInputStream("addressbook.bin")) {
readBook = AddressBook.parseFrom(fis);
}
// Print the entries
for (Map.Entry<String, Person> entry : readBook.getContactsMap().entrySet()) {
System.out.println("Email: " + entry.getKey());
System.out.println("Name: " + entry.getValue().getName());
System.out.println("Age: " + entry.getValue().getAge());
System.out.println("---");
}
}
}
2.3.1 Code Explanation
The provided Java code demonstrates how to use maps in Protobuf by creating an address book application. It begins by creating two Person objects, Alice and Bob, each with a name and age. These are then added to a Map<String, Person> field in the AddressBook using email IDs as keys. The address book is then serialized to a binary file named addressbook.bin. Later, this file is deserialized back into an AddressBook object. Finally, the code iterates through the contacts map and prints out each person’s email, name, and age to the console.
2.3.2 Code Output
When we run the above program, the output would be:
Email: alice@example.com Name: Alice Age: 30 --- Email: bob@example.com Name: Bob Age: 25
This confirms that the map field in Protobuf successfully stores and retrieves key-value pairs with custom message types. The program first serializes the AddressBook containing the map of email addresses to Person objects into a binary file using Protobuf’s writeTo method. It then reads the binary file using parseFrom, reconstructing the original AddressBook structure. The loop through the contacts map shows that all data is preserved accurately, verifying Protobuf’s ability to handle nested messages inside maps effectively and efficiently.
3. Conclusion
Protobuf maps are a powerful feature to represent key-value pairs compactly. In this article, we saw how to define maps in .proto files, use them in Java, and serialize/deserialize data with ease. This pattern is especially useful in modern systems where configuration, state, or user-defined data is naturally key-based.

