Medium👤 3-5 years👤 8-15 years 3 min read

Object Creation Cost & Primitive Streams — Interview Questions

Asked inAmazonGoogleMicrosoft
#object creation#autoboxing#intstream#escape analysis#performance
Report issue

⚡ Short Answer

Every object allocation is more than 'a bit of memory' — it's a write that has to be tracked by the garbage collector, and at high enough frequency, allocation rate (not heap size) becomes the dominant driver of GC pause frequency. Stream<Integer> pays this cost per element: each int gets autoboxed into an Integer object as it enters the pipeline. IntStream, LongStream, and DoubleStream exist specifically to avoid it — they carry primitives through the pipeline unboxed, and only box if you explicitly convert back to a boxed Stream<Integer> via .boxed().

Coffee Chat Question

Concept Made Simple

What's the real cost of excessive object creation, and how do IntStream/LongStream avoid the autoboxing tax Stream<Integer> pays?

🧠Mind Map Answer

Remember It Faster

The Integer cache (-128 to 127) hides this cost for small numbers in casual code, which is exactly why the problem is easy to miss until a hot path processes a wide range of values — outside the cache range, every autobox is a fresh heap allocation, one per element, on every pipeline execution.

Stream<Integer>Boxes every element — one allocation per int, outside the -128..127 cache
IntStream/LongStream/DoubleStreamPrimitives flow through unboxed — no per-element allocation
Escape analysisCan sometimes let the JIT stack-allocate short-lived, non-escaping objects
.boxed()Explicitly opts back into Integer objects when a Stream<Integer> API is needed

Key takeaway: in numeric hot paths, reach for the primitive stream types by default — mapToInt()/mapToLong() instead of a generic Stream<Integer>, saving an allocation per element without changing what the code actually computes.

⌨️Hands-on Keyboard

Learn by Doing

java
List<Integer> values = List.of(1, 2, 3, 200, 300);

// Boxes on the way in, boxes again for the intermediate reduce
int sumBoxed = values.stream().reduce(0, Integer::sum);

// mapToInt unboxes once, then the whole pipeline stays primitive
int sumPrimitive = values.stream().mapToInt(Integer::intValue).sum();

// The Integer cache hides the cost for small numbers, but not for these:
Integer a = 200, b = 200;
System.out.println(a == b);      // false — outside the -128..127 cache, real allocations

🔥What If?

Think Beyond the Expected

If escape analysis lets the JIT stack-allocate short-lived objects, does that make this whole concern moot?

Not reliably — escape analysis only applies when the JIT can prove an object never 'escapes' the method (isn't stored, returned, or passed somewhere it could outlive the method), and Stream pipeline internals with captured lambdas and iterator chaining often defeat that proof even when intuitively the object 'should' be short-lived. Using primitive streams removes the boxing allocation outright rather than hoping escape analysis optimizes it away.

😂Real World

This shows up in profiling as a surprisingly high allocation rate (visible in a GC/allocation profile) coming from a seemingly innocent numeric aggregation pipeline processing millions of records — switching Stream<Integer> to IntStream (or the equivalent for long/double) is a common, low-risk fix that shows up as a measurable drop in young-gen GC frequency.

🗣️Real Talk from Guru

When I see mapToInt/mapToLong show up unprompted in someone's Stream code, that tells me they've internalized that boxing isn't free — it's a small detail that correlates strongly with someone who's actually looked at an allocation profile rather than just read about Streams.

🎯Interviewer's Expectation

Keywords they're listening for:

Connects allocation rate to GC pause frequency, not just heap sizeExplains autoboxing as a per-element allocation in Stream<Integer>Names IntStream/LongStream/DoubleStream as the fixKnows the Integer cache's range and its limits

⚠️Common Mistakes

  • Using Stream<Integer> for numeric hot paths without considering IntStream
  • Assuming the Integer cache eliminates boxing costs generally
  • Not correlating high GC frequency with allocation rate in profiling

Best Practices

  • Use mapToInt/mapToLong/mapToDouble for numeric Stream pipelines
  • Profile allocation rate, not just heap size, when chasing GC-related latency
  • Reserve .boxed() for when a boxed-type API genuinely requires it

🔁Follow-up Questions

  • 1Why does the Integer cache only cover -128 to 127?
  • 2How would you find an unexpectedly high allocation rate in a running application?
  • 3When would you deliberately call .boxed() despite the extra allocation?

🧩Related Technologies

Escape AnalysisJITAutoboxing

Continue Learning with AI

Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly — AI is your learning companion, not a shortcut.

Plain-language foundations

I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner.

Topic: Primitives (Core Java)
Interview question: "What's the real cost of excessive object creation, and how do IntStream/LongStream avoid the autoboxing tax Stream<Integer> pays?"

Please:
1. Explain the core idea in simple, plain language, using an everyday analogy.
2. Define any technical terms you use.
3. Walk through one small, concrete example.
4. Finish with a single sentence I can easily remember.

Keep the tone friendly and assume I'm new to this topic.
Open inChatGPTGeminiClaude

Was this answer helpful?

Support our platform by exploring our recommended products.

As an Amazon affiliate, purchases through these links may earn us a small commission — at no extra cost to you. It helps keep Full Stack Interview Guru free.

Related Questions