10. జావా StringBuilder, StringBuffer & మెథడ్స్ (Mutable Strings Mastery)
గత 9వ పాఠంలో మనం జావాలోని String క్లాస్ యొక్క అత్యంత ప్రముఖ లక్షణమైన "ఇమ్మ్యుటబిలిటీ" (Immutability - మార్చలేని తత్వం) మరియు String Constant Pool (SCP) గురించి లోతుగా అర్థం చేసుకున్నాం. సాధారణ ప్రోగ్రాములలో భద్రత, హాషింగ్ మరియు థ్రెడ్ సేఫ్టీ కోసం ఇమ్మ్యుటబిలిటీ ఎంతో మేలు చేసినప్పటికీ, హై-పర్ఫార్మెన్స్ అప్లికేషన్లు లేదా భారీ లూపులలో వేలాది సార్లు స్ట్రింగ్స్ను సవరించాల్సి వచ్చినప్పుడు (String Modification/Concatenation) ఇది విపరీతమైన మెమరీ వృధాను సృష్టిస్తుంది. ప్రతి సవరణకు హీప్ మెమరీలో కొత్త ఆబ్జెక్ట్ ఏర్పడి, గార్బేజ్ కలెక్టర్ (GC) పై తీవ్రమైన భారం పడుతుంది. ఈ సంక్షోభాన్ని అధిగమించడానికి జావా రెండు అద్భుతమైన మ్యూటబుల్ క్లాసులను రూపొందించింది: సింగిల్-థ్రెడ్ వేగానికి మకుటం లేని రాజు StringBuilder మరియు మల్టీ-థ్రెడింగ్ పర్యావరణాల్లో సురక్షితమైన రక్షణ కవచం StringBuffer. వీటితో పాటు ఆధునిక జావా 15+ నుండి వచ్చిన విప్లవాత్మక Text Blocks (""") ఫీచర్ను ఈ పాఠంలో సంపూర్ణంగా నేర్చుకుందాం.
మట్టి కుండ శిల్పం & కార్ల అసెంబ్లీ రోబోట్ల అనాలజీ (The Clay Pottery & Mutable Assembly Robot Analogy):
- 1. String (ఎండిన రాతి శిల్పం - Immutable Stone Carving): ఒకసారి పాలరాతిపై శిల్పాన్ని చెక్కిన తర్వాత, దాని చేతిని లేదా ముక్కును మార్చడం అసాధ్యం. చిన్న మార్పు చేయాలన్నా, పాత శిల్పాన్ని పక్కన పారేసి, కొత్త రాయిని తెచ్చి మళ్లీ మొదటి నుండి మొత్తం చెక్కాల్సిందే. చిన్న చిన్న మార్పుల కోసం ఇది విపరీతమైన శ్రమ, సమయం మరియు మెటీరియల్ వృధా.
-
2. StringBuilder (మార్చడానికి వీలైన పచ్చి మట్టి ముద్ద - High-Speed Modifiable Clay): కుండలు చేసే బంకమట్టి ముద్ద పచ్చిగా ఉన్నప్పుడు, కావలసినట్లుగా అదనపు మట్టిని అతికించవచ్చు (
append), అనవసరమైన భాగాన్ని గీకి తీసేయవచ్చు (delete), లేదా ఆకారాన్ని పూర్తిగా తిరగరాయవచ్చు (reverse). దీనికి కొత్త మట్టి అవసరం లేదు; ఉన్న ముద్దలోనే మార్పులు జరుగుతాయి. సింగిల్-థ్రెడ్ కోడింగ్లో ఇది రాకెట్ వేగంతో పనిచేస్తుంది ఎందుకంటే దీనికి ఎటువంటి లాకింగ్ తనిఖీలు ఉండవు. -
3. StringBuffer (సెక్యూరిటీ గార్డులతో కూడిన ఫ్యాక్టరీ అసెంబ్లీ లైన్ - Thread-Safe Synchronized Factory): ఒకే సమయంలో బహుళ రోబోలు (Concurrent Threads) ఒకే కారును తయారు చేసేటప్పుడు, గందరగోళం జరగకుండా ప్రతి విభాగానికి ఒక సెక్యూరిటీ గార్డు (
synchronized lock) ఉంటాడు. ఒక రోబో పనిచేసే వరకు మిగిలిన రోబోలు వేచి ఉంటాయి. తద్వారా డేటా కరప్ట్ కాకుండా 100% థ్రెడ్-సేఫ్టీ లభిస్తుంది, అయితే సింక్రనైజేషన్ వల్ల కాస్త వేగం తగ్గుతుంది.
ఈ పాఠం పూర్తయ్యేసరికి మీరు లూపులలో మెమరీ లీక్లు లేకుండా వేగవంతమైన స్ట్రింగ్ ప్రాసెసింగ్ రాయడం, మల్టీ-థ్రెడింగ్ వాతావరణంలో తగిన మ్యూటబుల్ క్లాస్ను ఎంచుకోవడం, మరియు ప్రొడక్షన్-గ్రేడ్ JSON REST API సీరియలైజర్ను సొంతంగా నిర్మించడంలో నిపుణులవుతారు!
1. String కాంకాటనేషన్ సమస్య & StringBuilder అవసరం (The String Concatenation Bottleneck)
గత అధ్యాయంలో మనం చూసినట్లుగా, జావాలో String ఆబ్జెక్టులు శాశ్వతంగా మార్చలేనివి (Immutable). మీరు ఒక స్ట్రింగ్ను మరో స్ట్రింగ్తో + ఆపరేటర్ ఉపయోగించి కలిపినప్పుడు (Concatenation), జావా అసలు ఉన్న పాత స్ట్రింగ్ను మార్చదు. బదులుగా, హీప్ మెమరీలో రెండు విలువలనూ కలిపి సరికొత్త మూడవ ఆబ్జెక్ట్ను సృష్టిస్తుంది.
చిన్న చిన్న పనుల కోసం, ఉదాహరణకు రెండు లేదా మూడు పదాలను కలపడానికి (ఉదా: String fullName = firstName + " " + lastName;) ఇది ఎటువంటి సమస్యను కలిగించదు. ఎందుకంటే జావా కంపైలర్ (Java Compiler - javac) ఆధునిక వెర్షన్లలో సింపుల్ కాంకాటనేషన్లను అంతర్గతంగా invokedynamic లేదా StringBuilderగా మారుస్తుంది.
కానీ, అసలైన ప్రమాదం లూపులలో (Loops) సంభవిస్తుంది! లూప్లో వేలాది రికార్డులను ఒకే స్ట్రింగ్కు కలుపుతూ పోతే ఏమి జరుగుతుందో క్రింది ఉదాహరణ పరిశీలిద్దాం:
⚠️ ది టెరిబుల్ లూప్ కాంకాటనేషన్ ట్రాప్ (O(N²) Complexity Trap):
// అత్యంత ప్రమాదకరమైన కోడింగ్ పద్ధతి - ఎప్పటికీ రాయకండి!
String result = "";
for (int i = 0; i < 50000; i++) {
result += i; // ప్రతి ఇటరేషన్లోనూ సరికొత్త String ఆబ్జెక్ట్ పుడుతుంది!
}
పై లూప్ 50,000 సార్లు తిరిగినప్పుడు, మెమరీలో ఏకంగా 50,000 తాత్కాలిక String ఆబ్జెక్టులు సృష్టించబడతాయి. ప్రతి ఇటరేషన్లోనూ అంతకుముందు ఉన్న పొడవైన క్యారెక్టర్ ఎరే మొత్తాన్ని కొత్త ఎరేలోకి కాపీ చేయాలి. దీనివలన టైమ్ కాంప్లెక్సిటీ O(N²) అవుతుంది. ఈ వేలాది అనాథ ఆబ్జెక్టులను క్లీన్ చేయడానికి JVM గార్బేజ్ కలెక్టర్ (GC) అప్లికేషన్ మొత్తాన్ని క్షణకాలం నిలిపివేస్తుంది (Stop-the-World GC Pause). ఫలితంగా అప్లికేషన్ తీవ్రంగా మొరాయిస్తుంది!
ఈ విధ్వంసాన్ని ఆపడానికే జావా మ్యూటబుల్ బఫర్ (Mutable Character Buffer) సామర్థ్యం కలిగిన StringBuilder మరియు StringBuffer క్లాసులను java.lang ప్యాకేజీలో ప్రవేశపెట్టింది. ఇవి అంతర్గతంగా ఒకే రీసైజబుల్ క్యారెక్టర్ ఎరేను నిర్వహిస్తూ, అదే మెమరీలో మార్పులను చేయడానికి వీలు కల్పిస్తాయి.
2. StringBuilder vs StringBuffer సమగ్ర పోలిక (The Ultimate Showdown)
జావా ఇంటర్వ్యూలలో అత్యంత ప్రాచుర్యం పొందిన ప్రశ్నలలో ఒకటి: "What is the difference between String, StringBuilder, and StringBuffer?". జావా డెవలపర్గా ఈ మూడింటి మధ్య గల సూక్ష్మ వ్యత్యాసాలను, వాటి ఆర్కిటెక్చరల్ నిర్ణయాలను ఖచ్చితంగా తెలుసుకోవడం అత్యవసరం.
| ఫీచర్ / లక్షణం | String | StringBuilder | StringBuffer |
|---|---|---|---|
| మ్యూటబిలిటీ (Mutability) | Immutable (మార్చలేం) | Mutable (మార్చవచ్చు) | Mutable (మార్చవచ్చు) |
| పరిచయం చేయబడిన వెర్షన్ | Java 1.0 (మొదటి నుండి) | Java 5 (JDK 1.5) | Java 1.0 (లెగసీ క్లాస్) |
| థ్రెడ్ సేఫ్టీ (Thread-Safety) | అవును (ఇమ్మ్యుటబుల్ కావున 100% సేఫ్) | కాదు (Not Thread-Safe) | అవును (Synchronized Methods) |
| పనితీరు & వేగం (Performance) | లూపులలో అత్యంత నెమ్మది | అత్యంత వేగం (Blazing Fast) | మధ్యస్థం (లాకింగ్ ఓవర్హెడ్ వల్ల నెమ్మది) |
| మెమరీ లొకేషన్ | String Constant Pool & Heap | సాధారణ Heap మాత్రమే (No SCP) | సాధారణ Heap మాత్రమే (No SCP) |
| ఎప్పుడు ఉపయోగించాలి? | విలువలు స్థిరంగా ఉన్నప్పుడు, కీస్ గా | సింగిల్ త్రెడ్ లూపులలో, బిల్డర్లలో (95% కేసులు) | బహుళ త్రెడ్స్ ఒకే బఫర్ను షేర్ చేసుకున్నప్పుడు |
కీలకమైన అవగాహన: రియల్-వరల్డ్ సాఫ్ట్వేర్ డెవలప్మెంట్లో 95% సందర్భాలలో స్ట్రింగ్ మార్పులు ఒకే మెథడ్ పరిధిలో (Local Variables గా) జరుగుతాయి. లోకల్ వేరియబుల్స్ ఇప్పటికే ఒక్కో త్రెడ్ స్టాక్లో విడివిడిగా ఉంటాయి కాబట్టి వాటికి సింక్రనైజేషన్ అవసరం లేదు. అందువల్ల దాదాపు అన్ని చోట్లా StringBuilder వాడటమే ఉత్తమమైన ఇంజనీరింగ్ ప్రాక్టీస్!
3. డైనమిక్ Buffer కెపాసిటీ & గ్రోత్ అల్గారిథమ్ (Capacity Doubling Internals)
StringBuilder మరియు StringBuffer రెండూ కూడా అంతర్గతంగా AbstractStringBuilder అనే ఆబ్స్ట్రాక్ట్ క్లాస్ను ఎక్స్టెండ్ చేసుకుంటాయి. ఇవి పని చేసే విధానంలో అత్యంత ప్రధానమైన రెండు పదాలు: length() మరియు capacity().
length(): బఫర్లో ప్రస్తుతం ఉన్న అక్షరాల సంఖ్య (Characters actually stored).capacity(): కొత్త మెమరీని కేటాయించాల్సిన అవసరం లేకుండా ప్రస్తుత బఫర్ ఉంచుకోగల గరిష్ట అక్షరాల పరిమాణం (Total allocated slots).
డీఫాల్ట్ కెపాసిటీ & కన్స్ట్రక్టర్లు (Constructors):
StringBuilder sb1 = new StringBuilder(); // length = 0, capacity = 16
// 2. నిర్దిష్ట కెపాసిటీ కన్స్ట్రక్టర్ (బెస్ట్ పర్ఫార్మెన్స్)
StringBuilder sb2 = new StringBuilder(100); // length = 0, capacity = 100
// 3. స్ట్రింగ్ ఆధారిత కన్స్ట్రక్టర్: స్ట్రింగ్ పొడవు + 16 అదనపు స్లాట్లు
StringBuilder sb3 = new StringBuilder("Java"); // length = 4, capacity = 4 + 16 = 20
కెపాసిటీ రీఅలొకేషన్ సూత్రం (Dynamic Growth Formula):
మనం అక్షరాలను కలుపుకుంటూ వెళ్లేకొద్దీ, బఫర్ పరిమాణం సరిపోకపోతే JVM అంతర్గతంగా కొత్త పెద్ద ఎరేను కేటాయించి పాత అక్షరాలను కాపీ చేస్తుంది. ఆ కొత్త కెపాసిటీని ఈ క్రింది గణిత సూత్రంతో గణిస్తుంది:
ఉదాహరణకు, మీరు డీఫాల్ట్ new StringBuilder() తీసుకున్నారనుకుందాం (Capacity = 16). మీరు 17వ అక్షరాన్ని జోడించగానే:
New Capacity = (16 * 2) + 2 = 34 గా మారుతుంది.
35వ అక్షరాన్ని జోడించినప్పుడు:
New Capacity = (34 * 2) + 2 = 70 గా మారుతుంది.
Arrays.copyOf() జరిగి CPU సైకిల్స్ వృధా అవుతాయి. మీకు ముందే సుమారుగా ఎంత సైజు డేటా వస్తుందో తెలిస్తే (ఉదా: 5000 అక్షరాల JSON రెస్పాన్స్), ప్రారంభంలోనే new StringBuilder(5000); అని పేర్కొనడం వల్ల ఎటువంటి రీఅలొకేషన్లు లేకుండా అత్యధిక వేగం లభిస్తుంది!
4. Compact Strings & byte[] ఇంటర్నల్ ఆర్కిటెక్చర్ (Java 9+ Memory Optimization)
గతంలో Java 8 వరకు StringBuilder మరియు String రెండూ కూడా అక్షరాలను నిల్వ చేయడానికి 16-బిట్ char[] ఎరేను వాడేవి. అంటే ప్రతి ఒక్క అక్షరానికి 2 బైట్ల మెమరీ కేటాయించబడేది. ప్రపంచవ్యాప్తంగా నడిచే చాలా అప్లికేషన్లలోని స్ట్రింగ్స్ కేవలం ASCII లేదా Latin-1 (కేవలం 1 బైట్ సరిపోయే అక్షరాలు) మాత్రమే కలిగి ఉంటున్నాయని పరిశోధనలలో తేలింది.
అందువల్ల Java 9 (JEP 254: Compact Strings) నుండి ఆరాకిల్ ఇంజనీర్లు అంతర్గత నిర్మాణాన్ని విప్లవాత్మకంగా మార్చారు:
byte[] value; // char[] కు బదులుగా byte[] వాడకం
byte coder; // 0 = LATIN1 (1 byte per char), 1 = UTF16 (2 bytes per char)
int count; // వాస్తవ అక్షరాల సంఖ్య
ఈ మార్పు వలన మీ స్ట్రింగ్లో ఇంగ్లీష్ లేదా ల్యాటిన్ అక్షరాలు మాత్రమే ఉంటే, ప్రతి అక్షరానికి కేవలం 1 బైట్ మాత్రమే తీసుకుంటుంది. తెలుగు, హిందీ, ఎమోజీలు లేదా చైనీస్ వంటి ప్రత్యేక యూనికోడ్ అక్షరాలు వచ్చినప్పుడు మాత్రమే JVM ఆటోమేటిక్గా coder = UTF16 గా మార్చి ఒక్కో అక్షరానికి 2 బైట్లు కేటాయిస్తుంది. ఈ సరికొత్త ఆర్కిటెక్చర్ వల్ల ఆధునిక జావా అప్లికేషన్లలో హీప్ మెమరీ వినియోగం సుమారు 50% ఆదా అవుతుంది!
5. ముఖ్యమైన మెథడ్స్: append, insert, delete, replace (Core API Methods)
StringBuilder మరియు StringBuffer రెండూ ఒకే రకమైన మెథడ్ సంతకాలను (Method Signatures) కలిగి ఉంటాయి. నిత్యజీవితంలో అత్యంత విస్తృతంగా ఉపయోగించే నాలుగు ప్రధాన మెథడ్ల పనితీరును పరిశీలిద్దాం:
1. append(...) — చివర కలుపుట:
బఫర్ చివరిలో కొత్త డేటాను జోడించడానికి append() వాడతారు. ఇది అన్ని ప్రిమిటివ్ డేటా టైపులు (int, double, boolean, char), ఆబ్జెక్టులు మరియు స్ట్రింగ్స్తో ఓవర్లోడ్ చేయబడి ఉంది.
sb.append(" 21").append(" LTS"); // sb becomes "Java 21 LTS"
2. insert(int offset, ...) — మధ్యలో చొప్పించుట:
మనం కోరుకున్న నిర్దిష్ట ఇండెక్స్ (offset) వద్ద కొత్త అక్షరాలను చొప్పించడానికి ఇది ఉపయోగపడుతుంది. ఆ ఇండెక్స్ తర్వాత ఉన్న పాత అక్షరాలు కుడివైపుకు జరుపబడతాయి.
sb.insert(4, " 21"); // sb becomes "Java 21 Rocks"
3. delete(int start, int end) & deleteCharAt(int index) — తొలగించుట:
start ఇండెక్స్ నుండి end - 1 ఇండెక్స్ వరకు ఉన్న అక్షరాలను తొలగిస్తుంది. కేవలం ఒక్క అక్షరాన్ని మాత్రమే తొలగించాలంటే deleteCharAt(index) వాడతాం.
sb.delete(5, 11); // removes " World" -> sb becomes "Hello!"
sb.deleteCharAt(5); // removes '!' -> sb becomes "Hello"
4. replace(int start, int end, String str) — భర్తీ చేయుట:
ఇచ్చిన శ్రేణిలోని అక్షరాల స్థానంలో కొత్త స్ట్రింగ్ను రీప్లేస్ చేస్తుంది.
sb.replace(5, 12, "Evening"); // sb becomes "Good Evening"
6. reverse(), setCharAt() & Method Chaining (Fluent API Design)
స్ట్రింగ్ ఆపరేషన్లలో తరచూ ఉపయోగపడే మరికొన్ని అద్భుతమైన మెథడ్స్ మరియు ఫ్లూయెంట్ కోడింగ్ టెక్నిక్స్ గురించి తెలుసుకుందాం:
reverse() — తిరగరాయుట:
బఫర్లోని అక్షరాల క్రమాన్ని పూర్తిగా తిరగేస్తుంది. పాలిండ్రోమ్ (Palindrome - ఉదా: MADAM, 12321) ప్రోగ్రాములను సులభంగా పరిష్కరించడానికి ఇది అత్యుత్తమ షార్ట్కట్.
sb.reverse(); // sb remains "RADAR"
setCharAt(int index, char ch) & setLength(int newLength):
setCharAt(2, 'X'): నిర్దిష్ట ఇండెక్స్ వద్ద ఉన్న ఒక్క క్యారెక్టర్ను మాత్రమే మారుస్తుంది.setLength(0): బఫర్లోని మొత్తం కంటెంట్ను తుడిచివేసి ఖాళీ చేయడానికి (Clear buffer without creating new object) ఇది ఉత్తమ మార్గం!
మెథడ్ చైనింగ్ (Method Chaining / Fluent Pattern):
StringBuilder లోని దాదాపు అన్ని సవరణ మెథడ్స్ (append, insert, delete, reverse) చివరికి ఆబ్జెక్ట్ యొక్క ప్రస్తుత రిఫరెన్స్ను (return this;) తిరిగి ఇస్తాయి. అందువల్ల మనం ఒకే లైన్లో బహుళ మెథడ్లను గొలుసుకట్టుగా పిలవవచ్చు (Fluent Interface):
.append("=== STATUS REPORT ===")
.append(" Server: ").append("Active")
.append(" Latency: ").append(12).append("ms")
.toString(); // చివరగా String గా మార్చడం
7. Java 15+ Text Blocks (""") — మల్టీ-లైన్ స్ట్రింగ్స్ విప్లవం (Modern Java)
గతంలో జావాలో HTML, JSON లేదా SQL వంటి మల్టీ-లైన్ కంటెంట్ను రాయాలంటే డెవలపర్లకు నరకం కనిపించేది. ప్రతి లైన్ చివర
ఎస్కేప్ క్యారెక్టర్ పెట్టడం, డబుల్ కోట్స్ కోసం " వాడటం, మరియు ప్రతి లైన్ను + తో కలపడం చాలా గజిబిజిగా ఉండేది.
Java 15 (JEP 378) నుండి అధికారికంగా ప్రవేశపెట్టబడిన Text Blocks ఈ సమస్యను శాశ్వతంగా పరిష్కరించాయి! టెక్స్ట్ బ్లాక్ను మూడు డబుల్ కోట్స్ (""") తో ప్రారంభించి, మూడు డబుల్ కోట్స్ తో ముగిస్తాం.
String json = "{
" +
" "id": 101,
" +
" "name": "Java 21",
" +
" "status": "Active"
" +
"}";
String json = """
{
"id": 101,
"name": "Java 21",
"status": "Active"
}
""";
టెక్స్ట్ బ్లాక్స్ లో ముఖ్యమైన నియమాలు & ఎస్కేప్ సీక్వెన్స్లు:
- లైన్ బ్రేక్ నియమం: ఓపెనింగ్
"""తర్వాత అదే లైన్లో ఎటువంటి కంటెంట్ ఉండకూడదు. కచ్చితంగా న్యూలైన్ (Line Terminator) ఉండాలి. - ఆటోమేటిక్ ఇండెంటేషన్ స్ట్రిప్పింగ్: క్లోజింగ్
"""యొక్క అలైన్మెంట్ను బట్టి ఎడమవైపు ఉన్న కామన్ వైట్స్పేస్ను జావా కంపైలర్ ఆటోమేటిక్గా తీసివేస్తుంది (Incidental Indentation Stripping). (లైన్ కంటిన్యుయేషన్): కోడ్ చూసేందుకు రెండు లైన్లుగా కనిపించినా, ఫలితంలో న్యూలైన్ రాకూడదనుకుంటే లైన్ చివర బ్యాక్స్లాష్ () ఉంచుతాం.s(ట్రెయిలింగ్ స్పేస్ ప్రిజర్వేషన్): లైన్ చివర ఉండే స్పేస్లను కంపైలర్ తీసివేయకుండా కాపాడటానికిsవాడతాం.
8. String Formatting & .formatted() మెథడ్ (Modern String Interpolation)
జావాలో డైనమిక్ విలువలను స్ట్రింగ్ టెంప్లేట్లలో చొప్పించడానికి సాంప్రదాయకంగా String.format(template, args...) వాడేవారు. అయితే Java 15 లో Text Blocks రావడంతో పాటు, మరింత క్లీన్ మరియు రీడబుల్ కోడ్ రాయడానికి సరికొత్త ఇన్స్టాన్స్ మెథడ్ .formatted(...) ను చేర్చారు.
int unreadCount = 5;
double balance = 4590.75;
// ఆధునిక .formatted() మెథడ్ వాడకం:
String welcome = """ నమస్తే %s గారు! మీ వద్ద %d కొత్త నోటిఫికేషన్లు ఉన్నాయి. మీ అకౌంట్ బ్యాలెన్స్: ₹%,.2f """.formatted(user, unreadCount, balance);
ముఖ్యమైన ఫార్మాట్ స్పెసిఫైయర్లు (Format Specifiers):
• %s : String లేదా ఏదైనా ఆబ్జెక్ట్
• %d : డెసిమల్ పూర్ణాంకాలు (byte, short, int, long)
• %,.2f : ఫ్లోటింగ్ పాయింట్ నంబర్ను రెండు డెసిమల్స్తో, వేలల్లో కామాలతో (Commas grouping) ముద్రించడం
• %b : బూలియన్ విలువల కోసం
9. డెమో 1: StringBuilder vs String కాంకాటనేషన్ బెంచ్మార్క్ (Performance Benchmark)
సిద్ధాంతాన్ని పక్కన పెట్టి వాస్తవ పనితీరును కళ్లారా చూద్దాం! ఈ క్రింది ప్రోగ్రామ్లో 50,000 సార్లు String += మరియు StringBuilder.append() లను రన్ చేసి, వాటికి పట్టిన సమయం (Elapsed Nanoseconds / Milliseconds) మరియు మెమరీ వినియోగాన్ని నేరుగా లెక్కిస్తాం:
public class StringBuilderVsStringBenchmark {
public static void main(String[] args) {
final int ITERATIONS = 50_000;
System.out.println("=================================================");
System.out.println("🚀 JAVA STRING VS STRINGBUILDER BENCHMARK (50K)");
System.out.println("=================================================");
// 1. STRING CONCATENATION (+) BENCHMARK
System.gc(); // Clean garbage before test
long startMemString = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long startTimeString = System.nanoTime();
String strResult = "";
for (int i = 0; i < ITERATIONS; i++) {
strResult += "A";
}
long endTimeString = System.nanoTime();
long endMemString = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long durationStringMs = (endTimeString - startTimeString) / 1_000_000;
long memUsedStringKb = Math.max(0, (endMemString - startMemString) / 1024);
System.out.printf("1. String (+) Loop: %6d ms | Approx Mem: %6d KB%n", durationStringMs, memUsedStringKb);
// 2. STRINGBUILDER APPEND BENCHMARK
System.gc(); // Clean garbage before test
long startMemSb = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long startTimeSb = System.nanoTime();
StringBuilder sb = new StringBuilder(ITERATIONS); // Pre-sized capacity
for (int i = 0; i < ITERATIONS; i++) {
sb.append("A");
}
String sbResult = sb.toString();
long endTimeSb = System.nanoTime();
long endMemSb = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long durationSbMs = (endTimeSb - startTimeSb) / 1_000_000;
long memUsedSbKb = Math.max(0, (endMemSb - startMemSb) / 1024);
System.out.printf("2. StringBuilder.append: %6d ms | Approx Mem: %6d KB%n", durationSbMs, memUsedSbKb);
System.out.println("-------------------------------------------------");
if (durationSbMs > 0) {
double speedup = (double) durationStringMs / Math.max(1, durationSbMs);
System.out.printf("⚡ తీర్పు: StringBuilder సుమారు %.1f రెట్లు వేగవంతమైనది!%n", speedup);
} else {
System.out.println("⚡ తీర్పు: StringBuilder 1 ms లోపే పూర్తయింది! (వందల రెట్లు వేగం)");
}
System.out.println("=================================================");
}
}
================================================= 🚀 JAVA STRING VS STRINGBUILDER BENCHMARK (50K) ================================================= 1. String (+) Loop: 1428 ms | Approx Mem: 49120 KB 2. StringBuilder.append: 2 ms | Approx Mem: 110 KB ------------------------------------------------- ⚡ తీర్పు: StringBuilder సుమారు 714.0 రెట్లు వేగవంతమైనది! =================================================
📊 పరిశీలన: సాధారణ String += కి 1428 మిల్లీసెకన్లు మరియు సుమారు 49 MB తాత్కాలిక మెమరీ ఖర్చవగా, StringBuilder కేవలం 2 మిల్లీసెకన్లలో 110 KB మెమరీతో పని పూర్తి చేసింది! లూపులలో StringBuilder ఎందుకు తప్పనిసరో ఈ ఫలితాలు స్పష్టంగా రుజువు చేస్తున్నాయి.
10. డెమో 2: StringBuffer మల్టీ-థ్రెడింగ్ భద్రత (Race Condition & Thread-Safety)
ఒకే సమయంలో బహుళ త్రెడ్స్ (Multiple Threads) పనిచేసేటప్పుడు StringBuilder ఎందుకు విఫలమవుతుందో మరియు StringBuffer డేటాను ఎలా రక్షిస్తుందో నిరూపించే ప్రాక్టికల్ డెమో ఇది:
public class StringBufferThreadSafetyDemo {
public static void main(String[] args) throws InterruptedException {
final int APPENDS_PER_THREAD = 1000;
System.out.println("=================================================");
System.out.println("🛡️ MULTI-THREADING SAFETY TEST: BUILDER VS BUFFER");
System.out.println("=================================================");
// TEST 1: StringBuilder (Not Thread-Safe)
StringBuilder unsafeBuilder = new StringBuilder();
Thread t1 = new Thread(() -> {
for (int i = 0; i < APPENDS_PER_THREAD; i++) {
unsafeBuilder.append("A");
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < APPENDS_PER_THREAD; i++) {
unsafeBuilder.append("B");
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("1. StringBuilder (Expected: 2000 chars):");
System.out.println(" వాస్తవ పొడవు: " + unsafeBuilder.length());
if (unsafeBuilder.length() < 2000) {
System.out.println(" ❌ రేస్ కండిషన్ సంభవించింది! కొన్ని అక్షరాలు నష్టపోయాయి.");
} else {
System.out.println(" ⚠️ ఈ రన్లో కాకతాళీయంగా సరిపోయింది (కానీ గ్యారెంటీ లేదు).");
}
// TEST 2: StringBuffer (Thread-Safe with Synchronized Locks)
StringBuffer safeBuffer = new StringBuffer();
Thread t3 = new Thread(() -> {
for (int i = 0; i < APPENDS_PER_THREAD; i++) {
safeBuffer.append("A");
}
});
Thread t4 = new Thread(() -> {
for (int i = 0; i < APPENDS_PER_THREAD; i++) {
safeBuffer.append("B");
}
});
t3.start();
t4.start();
t3.join();
t4.join();
System.out.println("
2. StringBuffer (Expected: 2000 chars):");
System.out.println(" వాస్తవ పొడవు: " + safeBuffer.length());
if (safeBuffer.length() == 2000) {
System.out.println(" ✅ 100% థ్రెడ్-సేఫ్! మొత్తం 2000 అక్షరాలు భద్రంగా నిల్వ చేయబడ్డాయి.");
}
System.out.println("=================================================");
}
}
================================================= 🛡️ MULTI-THREADING SAFETY TEST: BUILDER VS BUFFER ================================================= 1. StringBuilder (Expected: 2000 chars): వాస్తవ పొడవు: 1842 ❌ రేస్ కండిషన్ సంభవించింది! కొన్ని అక్షరాలు నష్టపోయాయి. 2. StringBuffer (Expected: 2000 chars): వాస్తవ పొడవు: 2000 ✅ 100% థ్రెడ్-సేఫ్! మొత్తం 2000 అక్షరాలు భద్రంగా నిల్వ చేయబడ్డాయి. =================================================
🔒 విశ్లేషణ: StringBuilder లో రెండు త్రెడ్స్ ఒకేసారి అంతర్గత count వేరియబుల్ను మార్చడం వలన రేస్ కండిషన్ ఏర్పడి కొన్ని అక్షరాలు ఓవర్రైట్ అయిపోయాయి (1842 మాత్రమే మిగిలాయి). అదే StringBuffer లో ప్రతి append() మెథడ్ పై సింక్రనైజ్డ్ లాక్ ఉండటం వల్ల ఒక త్రెడ్ పూర్తయ్యేవరకు రెండో త్రెడ్ వేచి చూసి ఖచ్చితంగా 2000 అక్షరాలనూ భద్రపరిచింది!
11. Capstone ప్రాజెక్ట్: హై-త్రూపుట్ JSON REST API Serializer Engine
ఈ అధ్యాయంలో నేర్చుకున్న అన్ని కాన్సెప్టులను (StringBuilder, Pre-allocated Capacity, Method Chaining, Java 15 Text Blocks మరియు String Formatting) సమగ్రంగా మిళితం చేస్తూ ఒక రియల్-వరల్డ్ High-Throughput JSON Serializer & API Response Generator ను రూపొందిద్దాం. ఎక్స్టర్నల్ లైబ్రరీలు (Jackson, Gson) లేకుండానే స్వచ్ఛమైన జావా కోడ్తో మైక్రోసెకన్లలో JSON పేలోడ్లను నిర్మించే ఇంజిన్ ఇది:
public class JsonPayloadBuilderEngine {
// High-Performance Fluent JSON Builder using StringBuilder
public static class FastJsonBuilder {
private final StringBuilder sb;
private boolean firstField = true;
public FastJsonBuilder(int estimatedSize) {
this.sb = new StringBuilder(estimatedSize);
this.sb.append("{
");
}
public FastJsonBuilder addField(String key, String value) {
handleSeparator();
sb.append(" "").append(key).append("": "").append(escape(value)).append(""");
return this;
}
public FastJsonBuilder addField(String key, long value) {
handleSeparator();
sb.append(" "").append(key).append("": ").append(value);
return this;
}
public FastJsonBuilder addField(String key, double value) {
handleSeparator();
sb.append(" "").append(key).append("": ").append(value);
return this;
}
public FastJsonBuilder addField(String key, boolean value) {
handleSeparator();
sb.append(" "").append(key).append("": ").append(value);
return this;
}
public FastJsonBuilder addStringArray(String key, String[] items) {
handleSeparator();
sb.append(" "").append(key).append("": [");
for (int i = 0; i < items.length; i++) {
sb.append(""").append(escape(items[i])).append(""");
if (i < items.length - 1) sb.append(", ");
}
sb.append("]");
return this;
}
private void handleSeparator() {
if (!firstField) {
sb.append(",
");
}
firstField = false;
}
private String escape(String raw) {
return raw.replace("\", "\\").replace(""", "\"");
}
public String build() {
sb.append("
}");
return sb.toString();
}
public int currentCapacity() {
return sb.capacity();
}
}
public static void main(String[] args) {
System.out.println("=================================================");
System.out.println("⚡ HIGH-THROUGHPUT REST API JSON SERIALIZER ENGINE");
System.out.println("=================================================
");
// Step 1: Pre-allocating capacity to avoid resizing overhead
FastJsonBuilder builder = new FastJsonBuilder(512);
String[] roles = {"SUPER_ADMIN", "DEVELOPER", "ANALYST"};
// Step 2: Fluent Method Chaining
String jsonPayload = builder
.addField("service", "AuthGateway-v2")
.addField("userId", 8849204821L)
.addField("username", "balaji_dev")
.addField("accountBalance", 125400.50)
.addField("isMfaEnabled", true)
.addStringArray("assignedRoles", roles)
.build();
System.out.println("📦 Generated JSON Payload:");
System.out.println(jsonPayload);
// Step 3: Modern Java 15+ Text Block API Response Wrapper
String httpResponseTemplate = """
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: %d
X-Server-Engine: Java-21-LTS-CoreEngine
%s
""".formatted(jsonPayload.getBytes().length, jsonPayload);
System.out.println("
🌐 Complete HTTP REST Response Envelope:");
System.out.println(httpResponseTemplate);
System.out.println("-------------------------------------------------");
System.out.println("📊 Performance Telemetry:");
System.out.println(" Buffer Allocated Capacity: " + builder.currentCapacity() + " bytes");
System.out.println(" Final Payload Size: " + jsonPayload.length() + " chars");
System.out.println(" Zero Array Copies Reached: ✅ Optimal Memory State");
System.out.println("=================================================");
}
}
=================================================
⚡ HIGH-THROUGHPUT REST API JSON SERIALIZER ENGINE
=================================================
📦 Generated JSON Payload:
{
"service": "AuthGateway-v2",
"userId": 8849204821,
"username": "balaji_dev",
"accountBalance": 125400.5,
"isMfaEnabled": true,
"assignedRoles": ["SUPER_ADMIN", "DEVELOPER", "ANALYST"]
}
🌐 Complete HTTP REST Response Envelope:
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 201
X-Server-Engine: Java-21-LTS-CoreEngine
{
"service": "AuthGateway-v2",
"userId": 8849204821,
"username": "balaji_dev",
"accountBalance": 125400.5,
"isMfaEnabled": true,
"assignedRoles": ["SUPER_ADMIN", "DEVELOPER", "ANALYST"]
}
-------------------------------------------------
📊 Performance Telemetry:
Buffer Allocated Capacity: 512 bytes
Final Payload Size: 201 chars
Zero Array Copies Reached: ✅ Optimal Memory State
=================================================
12. ప్రొడక్షన్ పిట్ఫాల్స్, బెస్ట్ ప్రాక్టీసెస్ & 5 క్విజ్లు (Best Practices & Quizzes)
🚫 ప్రొడక్షన్లో నివారించాల్సిన 4 ఘోరమైన తప్పులు (Production Pitfalls):
-
లూపులలో
+=ఆపరేటర్ను వాడటం: ఇది ప్రతి చక్రంలోనూ సరికొత్త String ఆబ్జెక్ట్ను హీప్లో సృష్టిస్తుంది. లక్షలాది ట్రాన్సాక్షన్లు జరిగే సర్వర్లలో ఇది తీవ్రమైన అవుట్-ఆఫ్-మెమరీ (OutOfMemoryError) లేదా GC లాగ్స్ కు దారితీస్తుంది. -
సింగిల్-థ్రెడ్ కోడ్లోనూ అనవసరంగా
StringBufferవాడటం: చాలామంది పాత అలవాటు ప్రకారం StringBuffer వాడుతుంటారు. దీనివలన ప్రతి మెథడ్ కాల్ వద్ద నిష్ప్రయోజనమైన మ్యూటెక్స్ లాకింగ్ ఓవర్హెడ్ జరిగి CPU సైకిల్స్ వృధా అవుతాయి. సింగిల్ త్రెడ్ లో ఎల్లప్పుడూStringBuilderమాత్రమే వాడాలి. -
ప్రారంభ కెపాసిటీని అంచనా వేయకపోవడం: భారీ స్ట్రింగ్స్ నిర్మించేటప్పుడు డీఫాల్ట్ కెపాసిటీ (16) తో మొదలుపెడితే, బఫర్ పదే పదే డబుల్ అవుతూ అనేకసార్లు ఎరే రీఅలొకేషన్లు జరుగుతాయి. వీలైనంత వరకు అంచనా వేసిన సైజును కన్స్ట్రక్టర్లో ఇవ్వండి (
new StringBuilder(expectedSize)). -
బఫర్ను రీసెట్ చేయడానికి కొత్త ఆబ్జెక్ట్ సృష్టించడం: పాత బఫర్ను క్లియర్ చేయడానికి మళ్లీ
sb = new StringBuilder();అని రాయకండి. దానికి బదులుగాsb.setLength(0);వాడటం వలన కేటాయించిన పాత మెమరీ రీసైకిల్ అయి అత్యంత వేగంగా పనిచేస్తుంది.
✨ ప్రొడక్షన్ గోల్డెన్ రూల్స్ (Golden Rules):
String వాడండి.2. సింగిల్-థ్రెడ్ మ్యానిప్యులేషన్: లూపులు, క్వెరీ బిల్డర్లు, లాగ్ ఫార్మాటర్లకు
StringBuilder వాడండి.3. షేర్డ్ మల్టీ-థ్రెడింగ్: వేర్వేరు త్రెడ్స్ ఒకే బఫర్కు రాస్తుంటే
StringBuffer వాడండి.4. మల్టీ-లైన్ SQL / JSON / HTML: కోడ్ క్లీన్నెస్ కోసం జావా 15+
""" Text Blocks """ వాడండి.
🧠 మీ అవగాహనను పరీక్షించుకోండి (5 Interactive Quizzes):
ప్రశ్న 1: StringBuilder యొక్క డీఫాల్ట్ నో-ఆర్గ్ కన్స్ట్రక్టర్ (new StringBuilder()) ప్రారంభ కెపాసిటీ ఎంత?
సమాధానం: 16 అక్షరాలు (16 characters / slots).
ప్రశ్న 2: బఫర్ నిండిపోయినప్పుడు కొత్త కెపాసిటీని లెక్కించే జావా ఫార్ములా ఏమిటి?
సమాధానం: New Capacity = (Old Capacity * 2) + 2. ఉదాహరణకు పాత కెపాసిటీ 16 అయితే, కొత్తది (16 * 2) + 2 = 34 అవుతుంది.
ప్రశ్న 3: StringBuilder మరియు StringBuffer మధ్య ప్రధాన సాంకేతిక తేడా ఏమిటి?
సమాధానం: StringBuffer లోని మెథడ్స్ synchronized చేయబడి ఉంటాయి, కాబట్టి ఇది Thread-Safe. కానీ StringBuilder సింక్రనైజ్ కాలేదు (Not Thread-Safe), అందువల్ల సింగిల్ త్రెడ్ లో ఇది StringBuffer కంటే చాలా వేగంగా పనిచేస్తుంది.
ప్రశ్న 4: ఇప్పటికే ఉన్న StringBuilder ఆబ్జెక్ట్ను కొత్త మెమరీ అలొకేట్ చేయకుండా పూర్తిగా క్లియర్ చేయడానికి ఏ మెథడ్ వాడాలి?
సమాధానం: sb.setLength(0); వాడటం ద్వారా బఫర్ కౌంట్ను సున్నా చేసి పాత అలొకేటెడ్ ఎరేను సమర్థవంతంగా రీసైకిల్ చేయవచ్చు.
ప్రశ్న 5: జావా 15 Text Blocks (""") లో ఓపెనింగ్ ట్రిపుల్ కోట్స్ తర్వాత వెంటనే కంటెంట్ రాయవచ్చా?
సమాధానం: రాయకూడదు! ఓపెనింగ్ """ తర్వాత తప్పనిసరిగా లైన్ టెర్మినేటర్ (New Line) మాత్రమే ఉండాలి, లేకపోతే కంపైలేషన్ ఎర్రర్ వస్తుంది.