Skip to content
JavaAgentic

Type at least two characters. Try “RAG”, “pgvector” or “tool calling”.

Functional Java — AI pipelines ke liye streams aur lambdas

Functional Java sirf utna jitna AI kaam mein lagta hai: document pipeline ke liye streams, missing metadata ke liye Optional, aur parallel model calls ke liye CompletableFuture.

Beginner4 min ka padhnaUpdate hua
Is page par

Functional Java yahan theory ke liye nahi hai. Document ingestion, embedding pipeline aur ek saath kai model calls — ye teenon kaam apne aap streams aur futures ki shakal mein aate hain. Isliye yahan poora functional programming nahi padhaya ja raha, sirf wo hissa jo aap AI code mein sach mein likhenge.

Key Takeaways

  • Stream us kaam ke liye hai jo "load karo → filter karo → transform karo → collect karo" jaisa dikhta ho.
  • Optional model ke aadhe-adhoore metadata ko safely handle karne ka tareeka hai.
  • CompletableFuture tab lijiye jab kai model calls ek doosre par depend karti hon.
  • Har jagah functional style thopna galti hai — jahan loop saaf hai, wahan loop hi likhiye.

Lambda ko ek baar aur seedha samajh lijiye

Lambda ka matlab bas itna hai: "function ko ek value ki tarah paas karna". Pehle iske liye poori anonymous class likhni padti thi:

documents.sort(new Comparator<Document>() {
    @Override
    public int compare(Document a, Document b) {
        return a.title().compareTo(b.title());
    }
});

Ab wahi baat:

documents.sort(Comparator.comparing(Document::title));

Document::title ko method reference kehte hain — matlab "har document par title() chala do". Jab lambda ka poora kaam hi ek existing method ko call karna ho, tab method reference lambda se saaf padha jaata hai.

Stream — ingestion pipeline ki natural shakal

RAG ke liye documents taiyaar karne ka kaam hamesha ek jaisa hota hai: files padho, khaali waale hatao, chunks mein todo, aur ek list banao. Loop mein ye kaam teen nested blocks mein bikhar jaata hai. Stream mein wo bilkul waise hi likha jaata hai jaise aap bolte:

List<TextSegment> segments = documents.stream()
    .filter(doc -> doc.text() != null && !doc.text().isBlank())
    .map(splitter::split)
    .flatMap(List::stream)
    .toList();

Line by line padhiye: khaali documents hata do, har document ko chunks mein todo, sabhi chunk-lists ko ek hi flat list bana do, aur list le lo.

flatMap par ek second rukiye, kyunki yahi wo jagah hai jahan log atakte hain. map ke baad aapke paas List<List<TextSegment>> tha — har document ke liye ek alag list. flatMap un sab ko kholkar ek hi list bana deta hai. Ingestion pipeline mein ye step lagbhag hamesha aata hai.

Grouping bhi aksar chahiye hoti hai — jaise source ke hisaab se documents ginna:

Map<String, Long> perSource = documents.stream()
    .collect(Collectors.groupingBy(Document::source, Collectors.counting()));

Optional — "shayad hai, shayad nahi"

Model response ka metadata bharosemand nahi hota. Kabhi token usage aata hai, kabhi nahi. Seedha .getTokens() maar denge to ek din production mein null milega.

int tokens = Optional.ofNullable(response.metadata())
    .map(Metadata::usage)
    .map(Usage::totalTokens)
    .orElse(0);

Beech ka koi bhi step null nikla to chain wahin ruk jaati hai aur 0 mil jaata hai. Koi if (x != null) ki seedhi nahi.

Do choti baatein jo Optional ko galat use hone se bachati hain:

  • Optional ko method return karne ke liye use kijiye, field ya method parameter ke liye nahi.
  • .get() mat lagaiye. Hamesha .orElse(...), .orElseGet(...) ya .orElseThrow(...) lijiye — warna Optional sirf naya naam hai NullPointerException ka.

CompletableFuture — jab calls ek doosre par depend karti hain

Do independent model calls ek saath bhejni hain? Sabse aasaan tareeka:

CompletableFuture<String> summary =
    CompletableFuture.supplyAsync(() -> chat.prompt().user(summarize).call().content());
 
CompletableFuture<String> keywords =
    CompletableFuture.supplyAsync(() -> chat.prompt().user(extract).call().content());
 
String result = summary.thenCombine(keywords, (s, k) -> s + "\n\nKeywords: " + k).join();

thenCombine ka matlab hai "dono ka intezaar karo, phir dono ke result se ye banao". Agar ek call ka result doosri call mein chahiye, to thenCompose lagta hai:

CompletableFuture<String> answer =
    CompletableFuture.supplyAsync(() -> retriever.findContext(question))
        .thenCompose(context -> CompletableFuture.supplyAsync(
            () -> chat.prompt().system(context).user(question).call().content()));

Aur AI calls fail hoti hain — rate limit, timeout, provider down. Isliye fallback aur timeout hamesha lagaiye:

String safe = answer
    .orTimeout(20, TimeUnit.SECONDS)
    .exceptionally(ex -> "Abhi jawab nahi mil paaya, thodi der baad try kijiye.")
    .join();

Kab functional style mat likhiye

Ye utna hi zaroori hai jitna upar wala sab kuch:

  • Beech mein se nikalna ho (break) — loop lijiye.
  • Index chahiye ho — loop lijiye.
  • Har element par checked exception fenkne wala kaam ho — stream mein wo bahut ganda dikhta hai.
  • Nested lambda teen level gehri ho gayi ho — code review mein koi nahi padhega. Todiye.

Rule simple hai: functional style tab hi jeetti hai jab wo intent saaf kare. Jahan wo intent chhupa rahi ho, wahan wo galat choice hai — chahe kitni bhi modern lage.

Aage kya

Ab aapke paas wo saara functional toolkit hai jo document pipeline aur concurrent model calls mein lagta hai. Isse aage streaming aati hai — jahan jawab ek saath nahi, token-by-token aata hai — aur wahan Project Reactor ki baari hai.

Frequently Asked Questions

Document processing ke liye stream lein ya simple for-loop?
Jab kaam ek pipeline jaisa ho — load karo, filter karo, transform karo, collect karo — tab stream padhne mein saaf lagta hai, kyunki code bilkul waise hi steps mein dikhta hai. Jab beech mein se nikalna ho, index chahiye ho, ya complicated if-else ho, tab simple loop hi behtar hai. Stream ko zabardasti mat thopiye.
Parallel model calls ke liye CompletableFuture use karein ya virtual threads?
Java 21 par simple fan-out ke liye virtual threads zyada aasaan hain — normal blocking code likhiye aur baat khatam. CompletableFuture tab kaam aata hai jab stages ek doosre par depend karte hain: pehle A call karo, uske result se B aur C call karo, phir dono ko jodo. Timeout aur fallback bhi usmein saaf jud jaate hain.
AI code mein Optional ki zaroorat kyun padti hai?
Model ke response ka metadata bharosemand nahi hota. Token usage, finish reason, tool calls — ye fields kabhi aate hain kabhi nahi. Optional aapko majboor karta hai ki aap "nahi mila" wali situation ko explicitly handle karein, warna ek din wahi ek response aayega jisme field missing hai aur production mein NullPointerException padega.

Milte-julte tutorials