Design PatternsStructural

Flyweight

Share fine-grained objects to support large numbers efficiently.

3 min readDesigns · LLD & Patterns

Intent

Reduce memory footprint by sharing the parts of an object that don't change between instances.

Use when:

  • You have a huge number of similar objects.
  • Most of each object's state is identical between instances.
  • Memory is tight (mobile, embedded, big data).

Intrinsic vs Extrinsic State

The trick is splitting state into two buckets:

StateDescriptionExample (forest of trees)
IntrinsicShared, immutable, lives in the flyweightTree species: name, texture, mesh
ExtrinsicPer-instance, passed in by callerPosition (x, y), height, age
Intrinsic vs Extrinsic StateFlowchart

The forest renders 1 million trees, but holds only ~10 species objects in memory.


Structure

StructureClass diagram

Example: Forest with Millions of Trees

// Flyweight — shared, immutable
public class TreeSpecies {
    private final String name;
    private final byte[] texture;     // big!
    private final Mesh mesh;

    public TreeSpecies(String name, byte[] texture, Mesh mesh) {
        this.name = name;
        this.texture = texture;
        this.mesh = mesh;
    }

    public void render(Canvas c, int x, int y, double height) {
        c.draw(mesh, texture, x, y, height);
    }
}

// Factory — interns flyweights
public class TreeSpeciesFactory {
    private final Map<String, TreeSpecies> pool = new HashMap<>();

    public TreeSpecies get(String name, byte[] texture, Mesh mesh) {
        return pool.computeIfAbsent(name, k -> new TreeSpecies(name, texture, mesh));
    }
}

// Per-instance object — small
public class Tree {
    private final int x, y;
    private final double height;
    private final TreeSpecies species;     // shared reference

    public Tree(int x, int y, double height, TreeSpecies species) {
        this.x = x; this.y = y; this.height = height; this.species = species;
    }

    public void render(Canvas c) {
        species.render(c, x, y, height);   // pass extrinsic state in
    }
}

// Forest
public class Forest {
    private final List<Tree> trees = new ArrayList<>();
    private final TreeSpeciesFactory factory = new TreeSpeciesFactory();

    public void plant(int x, int y, double height, String type, byte[] tex, Mesh mesh) {
        TreeSpecies species = factory.get(type, tex, mesh);
        trees.add(new Tree(x, y, height, species));
    }
}

A million trees with 4 species: 1 million small Tree objects (a few bytes each) + 4 large TreeSpecies objects. Without flyweight, each tree would carry its own copy of the 1 MB texture — terabytes of waste.


Real-world Examples

Java's Integer.valueOf()

Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
System.out.println(a == b);   // true — flyweight cache for -128..127

Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(c == d);   // false — outside cache range

String.intern()

Calling intern() on a string returns a canonical reference from a JVM-wide pool. Two interned strings with the same content share memory.

Other examples

Use caseIntrinsicExtrinsic
Text editorsGlyph (font + char)Position
Game spritesSprite sheetPosition, rotation
Chess boardPiece typeSquare coordinates
HTML renderingDOM element typeAttributes, position

Flyweight vs Singleton

FlyweightSingleton
Number of instancesMany (one per intrinsic value)Exactly one
GoalMemory savingsSingle global access
StateImmutable intrinsicStateful, often mutable

A flyweight factory may have hundreds of cached instances. A singleton has exactly one.


Cautions

  • Flyweights must be immutable. If thread A mutates a shared flyweight, thread B's data corrupts. Make all flyweight fields final.
  • Don't apply prematurely. Flyweight only pays off when you have many objects with much shared state. For 100 objects, the factory overhead can outweigh the savings.
  • Hashcode/equality matter. The factory's pool keys on identity of intrinsic state — get this right or you'll create duplicates.

Trade-offs

✅ Pros:

  • Massive memory reduction for large object populations
  • Faster construction (cache hits skip allocation)
  • Encourages immutability of shared parts

❌ Cons:

  • Splitting intrinsic vs extrinsic complicates the API
  • Caller has to pass extrinsic state explicitly
  • Threading: shared state must be immutable
  • Adds factory indirection

Interview Tips

  • Flag flyweight when the interviewer mentions "millions of", "memory-constrained", or "each X has the same Y".
  • Use the forest / tree species example — it's instantly understood.
  • Mention Integer.valueOf cache as a familiar real-world example.

Finished reading?

Mark it done to track your progress in Designs.