Implementation:Apache Beam StructuralKey
| Knowledge Sources | |
|---|---|
| Domains | Data_Processing, Runner_Infrastructure |
| Last Updated | 2026-02-09 00:00 GMT |
Overview
Concrete wrapper class that provides coder-based structural equality and hashing for keys used in GroupByKey operations within the Apache Beam local runner.
Description
The StructuralKey abstract class wraps a key value together with its Coder to implement equals() and hashCode() based on the coder's structural value (via Coder.structuralValue()) rather than the key's native Java object equality. This is critical because Beam's model requires that key equality be determined by the coder's serialization semantics, not by Java's Object.equals(). The class provides two factory methods: of(key, coder) for creating keyed instances that lazily compute their encoded bytes and structural value, and empty() for representing keyless bundles where all instances are considered equal.
Usage
Use this class when grouping or looking up elements by key in the local runner's internal data structures. It is consumed by Bundle, CommittedResult, and EvaluationContext to ensure correct key-based grouping semantics. Not typically imported by end users; it is a runner-internal utility.
Code Reference
Source Location
- Repository: Apache_Beam
- File: runners/local-java/src/main/java/org/apache/beam/runners/local/StructuralKey.java
- Lines: 18-112
Signature
public abstract class StructuralKey<K> {
/** Returns the key this StructuralKey was created from. */
public abstract K getKey();
/** Get the empty StructuralKey. All empty keys are considered equal. */
public static StructuralKey<?> empty();
/**
* Create a new StructuralKey from the provided key and coder.
* @param key the key value
* @param coder the coder that defines structural equality
* @return a StructuralKey wrapping the key
* @throws IllegalArgumentException if the key cannot be encoded
*/
public static <K> StructuralKey<K> of(K key, Coder<K> coder);
}
Import
import org.apache.beam.runners.local.StructuralKey;
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| key | K | Yes (for of()) | The key value to wrap |
| coder | Coder<K> | Yes (for of()) | The coder that defines structural equality semantics for the key |
Outputs
| Name | Type | Description |
|---|---|---|
| of() returns | StructuralKey<K> | A StructuralKey that uses coder-based structural equality |
| empty() returns | StructuralKey<?> | A sentinel StructuralKey where all instances are equal |
| getKey() | K | The original key value (decoded from bytes for CoderStructuralKey) |
| equals() | boolean | True if the structural values of both keys are equal per their coders |
| hashCode() | int | Hash code derived from the coder's structural value |
Usage Examples
Creating and Comparing Structural Keys
import org.apache.beam.runners.local.StructuralKey;
import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.coders.VarIntCoder;
// Create structural keys from values and their coders
StructuralKey<String> key1 = StructuralKey.of("hello", StringUtf8Coder.of());
StructuralKey<String> key2 = StructuralKey.of("hello", StringUtf8Coder.of());
// Structural equality: same coder + same value => equal
assert key1.equals(key2);
assert key1.hashCode() == key2.hashCode();
// Retrieve the original key
String originalKey = key1.getKey(); // "hello"
// Empty keys for keyless bundles
StructuralKey<?> empty1 = StructuralKey.empty();
StructuralKey<?> empty2 = StructuralKey.empty();
// Note: empty keys are anonymous objects, equality depends on implementation
Using StructuralKey in a Map
import org.apache.beam.runners.local.StructuralKey;
import org.apache.beam.sdk.coders.VarIntCoder;
import java.util.HashMap;
import java.util.Map;
// StructuralKey can be used as a HashMap key because it
// implements equals/hashCode via coder structural value
Map<StructuralKey<Integer>, List<String>> groupedData = new HashMap<>();
StructuralKey<Integer> key = StructuralKey.of(42, VarIntCoder.of());
groupedData.computeIfAbsent(key, k -> new ArrayList<>()).add("value1");
// Looking up with a structurally equal key works correctly
StructuralKey<Integer> lookupKey = StructuralKey.of(42, VarIntCoder.of());
List<String> values = groupedData.get(lookupKey); // ["value1"]