liveBook · Manning

Composite key

As explained in The Art of Code

A composite key is a key formed from multiple values that together identify an item or entry.

A composite key combines multiple values into one key that identifies a map or set entry. It makes a relationship explicit when neither value alone is sufficient, and can replace a nested map whose structure hides the meaning of the grouping. section 4.2.2 section 4.3

Use a small immutable data carrier when the combined values naturally belong together. A named key such as AgeBmiKey communicates that an entry is counted by both age range and BMI category; Map<AgeBmiKey, Long> exposes that relationship directly, unlike Map<Age, Map<BmiCategory, Long>>. section 4.2.2 section 4.3

Counting BMI categories by age range

Create record AgeBmiKey(Age age, BmiCategory category) {}. For each physical profile, derive its ten-year age range and BMI category, construct an AgeBmiKey, and group profiles by that key. Each resulting entry contains the complete identity needed to calculate a BmiStat or find the age range with the most obese profiles. section 4.2.2

Listing 4.5 Refactored implementation with clear data modeling
public BmiGlobalResult computeBmiStats(List<PhysicalProfile> profiles) {

  long totalUsers = profiles.size();

  Map<AgeBmiKey, Long> countByAgeBmiKey = profiles.stream()
     .collect(Collectors.groupingBy(
        p -> new AgeBmiKey(
           new Age((p.age().value() / 10) * 10),
           calculateBmiCategory(p)
        ),
        Collectors.counting()
     ));

  List<BmiStat> stats = countByAgeBmiKey.entrySet().stream()
     .map(entry -> new BmiStat(
        entry.getKey().age(),
        entry.getKey().category(),
        new Percentage(totalUsers == 0 ? 0.0 : 
          100.0 * entry.getValue() / totalUsers)
     ))
     .toList();

  Age ageWithMostObese = countByAgeBmiKey.entrySet().stream()
     .filter(entry -> entry.getKey().category() == BmiCategory.OBESE)
     .max(Comparator.comparingLong(Map.Entry::getValue))
     .map(Map.Entry::getKey)
     .map(AgeBmiKey::age)
     .orElse(null);

  return new BmiGlobalResult(totalUsers, ageWithMostObese, stats);
}

This refactoring shows the composite key at the collection boundary: grouping produces one flat map, while later transformations read age and category from the named key. section 4.2.2

Model a composite key when it improves the reader’s ability to see the data’s identity:

  • Name the carrier after the relationship it represents, rather than leaving the relationship implied by nested collection types. section 4.2.2 section 4.3
  • Keep it small and immutable when it represents a stable combined identifier. section 4.2.2 section 4.3
  • Flatten nested maps when the flatter key makes the grouping easier to reason about. section 4.3
  • Do not add a type merely for ceremony; prefer the simpler model when extra structure adds little understanding. section 4.3

A composite key is not an automatic replacement for every nested structure. Introduce it when the pair has meaningful identity, reduces ambiguity, or makes transformations clearer; leave the data flatter only when that precision earns its added structure. section 4.3

How The Art of Code covers it

How deep it goes
Works through a Java record key in a BMI grouping refactoring, without detailing map-key equality internals.
Examples
Uses AgeBmiKey to count physical profiles by ten-year age range and BMI category.
What it assumes
Assumes familiarity with maps, sets, Java records, and grouped collection processing.
What you can do after
Apply it via Java records to replace a nested BMI-count map with a flat keyed map.
How it connects
Connects composite keys with records, immutability, nested maps, data modeling, and enums.

Also explained in

Compare how 8 books explain composite key →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Art of Code. The section links point to the parts of the book it draws on.