जावा में संदर्भ द्वारा एक स्ट्रिंग पास करना?


161

मुझे निम्नलिखित करने की आदत है C:

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

और आउटपुट है:

foo

हालांकि, जावा में, यह काम नहीं करता है। मुझे लगता है क्योंकि Stringऑब्जेक्ट को संदर्भित द्वारा पारित करने के बजाय कॉपी किया गया है । मुझे लगा कि स्ट्रिंग्स ऑब्जेक्ट्स थे, जिन्हें हमेशा संदर्भ द्वारा पारित किया जाता है।

यहाँ क्या हो रहा है?


2
भले ही वे संदर्भ द्वारा पारित किए गए थे (जावा में जो पास हो गया है वह संदर्भ मूल्य की एक प्रति है लेकिन यह एक और धागा है) स्ट्रिंग ऑब्जेक्ट अपरिवर्तनीय हैं, इसलिए यह वैसे भी काम नहीं करेगा
OscarRyz

8
यह C कोड नहीं है।
अल्स्टन

@Phil: यह C # में भी काम नहीं करेगा।
मंगेश

जवाबों:


196

आपके पास तीन विकल्प हैं:

  1. स्ट्रिंग स्ट्रिंग का उपयोग करें:

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
  2. कंटेनर क्लास बनाएँ और कंटेनर के एक उदाहरण को अपनी विधि में पास करें:

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
  3. एक सरणी बनाएँ:

    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }

प्रदर्शन के दृष्टिकोण से, स्ट्रिंगब्यूलर आमतौर पर सबसे अच्छा विकल्प होता है।


11
बस ध्यान रखें कि StringBuilder धागा सुरक्षित नहीं है। मल्टीथ्रेडेड वातावरण में स्ट्रिंग स्ट्रिंगर वर्ग का उपयोग करें या मैन्युअल रूप से सिंक्रनाइज़ेशन का ख्याल रखें।
बोरिस पावलोवीक

12
@ बोरिस पावलोविच - हाँ, लेकिन जब तक एक ही स्ट्रिंगबर्स्टल का उपयोग विभिन्न थ्रेड्स द्वारा नहीं किया जाता है, जो किसी भी परिदृश्य में आईएमओ की संभावना नहीं है, यह एक समस्या नहीं होनी चाहिए। यह अलग बात नहीं है अगर विधि को अलग-अलग थ्रेड द्वारा बुलाया जाता है, अलग-अलग स्ट्रिंगबुलस्ट प्राप्त करता है।
रवि वलाउ

मुझे तीसरा विकल्प (सरणी) सबसे अधिक पसंद है क्योंकि यह एकमात्र सामान्य है। यह बूलियन, इंट, आदि को भी पास करने की अनुमति देता है। क्या आप इस समाधान के प्रदर्शन के विवरण में थोड़ी और व्याख्या कर सकते हैं - आप दावा करते हैं कि पहले एक प्रदर्शन के दृष्टिकोण से बेहतर है।
मेओलिक

@meolic StringBuilderतेज़ है s += "..."क्योंकि यह हर बार नई स्ट्रिंग ऑब्जेक्ट आवंटित नहीं करता है। वास्तव में जब आप जावा कोड लिखते हैं s = "Hello " + name, तो कंपाइलर बाइट कोड उत्पन्न करेगा जो दो बार StringBuilderकॉल करता है append()
आरोन दिगुल्ला

86

जावा में कुछ भी संदर्भ से पारित नहीं होता है । सब कुछ मूल्य द्वारा पारित किया जाता है । वस्तु संदर्भ मूल्य द्वारा पारित किए जाते हैं। इसके अतिरिक्त स्ट्रिंग्स अपरिवर्तनीय हैं । इसलिए जब आप उत्तीर्ण स्ट्रिंग में जाते हैं तो आपको एक नया स्ट्रिंग प्राप्त होता है। आप एक वापसी मूल्य का उपयोग कर सकते हैं, या इसके बजाय एक स्ट्रिंगबर्गर पास कर सकते हैं।


2
यह एक गलत धारणा नहीं है यह एक अवधारणा है
पैट्रिक कॉर्नेलिसन

41
उम्म..नो, यह एक गलत धारणा है कि आप किसी वस्तु को संदर्भ से गुजार रहे हैं। आप मान द्वारा एक संदर्भ पास कर रहे हैं।
एड एस।

30
हां, यह एक गलत धारणा है। यह एक विशाल, व्यापक भ्रांति है। यह एक साक्षात्कार प्रश्न से मुझे नफरत है: ("जावा पास तर्क कैसे देता है")। मैं इसे नफरत करता हूं क्योंकि लगभग आधे साक्षात्कारकर्ता वास्तव में गलत उत्तर चाहते हैं ("मूल्य द्वारा आदिम, संदर्भ द्वारा वस्तुएं")। सही उत्तर देने में अधिक समय लगता है, और उनमें से कुछ को भ्रमित करने के लिए लगता है। और वे आश्वस्त नहीं होंगे: मैं कसम खाता हूं कि मैंने एक तकनीकी स्क्रीन को फूंका था क्योंकि CSMajor-type स्क्रेनर ने कॉलेज में गलत धारणा को सुना था और इसे सुसमाचार के रूप में माना था। Feh।
CPerkins

4
@ सहकर्मी आपको पता नहीं है कि मुझे कितना गुस्सा आता है। यह मेरे खून को उबाल देता है।

6
सभी भाषाओं में मूल्य द्वारा सब कुछ पारित किया जाता है। उनमें से कुछ मान पते (संदर्भ) होते हैं।
gerardw

52

क्या हो रहा है कि संदर्भ मूल्य से पारित किया जाता है, अर्थात, संदर्भ की एक प्रति पारित की जाती है। जावा में कुछ भी संदर्भ द्वारा पारित नहीं किया गया है, और चूंकि एक स्ट्रिंग अपरिवर्तनीय है, इसलिए असाइनमेंट एक नया स्ट्रिंग ऑब्जेक्ट बनाता है जो संदर्भ की प्रतिलिपि बनाता है अब इंगित करता है। मूल संदर्भ अभी भी खाली स्ट्रिंग को इंगित करता है।

यह किसी भी वस्तु के लिए एक ही होगा, अर्थात, इसे एक विधि में नए मूल्य पर सेट करना। नीचे दिए गए उदाहरण से यह पता चलता है कि क्या अधिक स्पष्ट है, लेकिन एक स्ट्रिंग को संक्षिप्त करना वास्तव में एक ही बात है।

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}

6
बहुत बढ़िया उदाहरण!
निकोलस

18

java.lang.String अपरिवर्तनीय है।

मुझे URL चिपकाने से नफरत है लेकिन https://docs.oracle.com/javase/10/docs/api/java/lang/String.html आपके लिए जावा-लैंड में होने के कारण पढ़ना और समझना आवश्यक है।


मुझे नहीं लगता कि सोरेन जॉनसन को पता नहीं है कि स्ट्रिंग क्या है, यह सवाल नहीं था और नहीं कि मैं जावा के 15 साल बाद यहां क्या देख रहा था।
एएचएच

8

वस्तुओं को संदर्भ द्वारा पारित किया जाता है, आदिम मूल्य द्वारा पारित किया जाता है।

स्ट्रिंग एक आदिम नहीं है, यह एक वस्तु है, और यह वस्तु का एक विशेष मामला है।

यह मेमोरी-सेविंग उद्देश्य के लिए है। जेवीएम में, एक स्ट्रिंग पूल है। बनाए गए प्रत्येक स्ट्रिंग के लिए, JVM यह देखने की कोशिश करेगा कि क्या स्ट्रिंग पूल में समान स्ट्रिंग मौजूद है, और यदि पहले से ही एक थे तो इसे इंगित करें।

public class TestString
{
    private static String a = "hello world";
    private static String b = "hello world";
    private static String c = "hello " + "world";
    private static String d = new String("hello world");

    private static Object o1 = new Object();
    private static Object o2 = new Object();

    public static void main(String[] args)
    {
        System.out.println("a==b:"+(a == b));
        System.out.println("a==c:"+(a == c));
        System.out.println("a==d:"+(a == d));
        System.out.println("a.equals(d):"+(a.equals(d)));
        System.out.println("o1==o2:"+(o1 == o2));

        passString(a);
        passString(d);
    }

    public static void passString(String s)
    {
        System.out.println("passString:"+(a == s));
    }
}

/ * OUTPUT * /

a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false

== मेमोरी एड्रेस (संदर्भ) के लिए जाँच कर रहा है, और असमान सामग्री (मूल्य) के लिए जाँच कर रहा है


3
पूरी तरह से सच नहीं है। जावा हमेशा VALUE द्वारा पास होता है; चाल यह है कि वस्तुओं को संदर्भ द्वारा पारित किया जाता है जो मूल्य से गुजरते हैं। (मुश्किल, मुझे पता है)। इसे एक बार देखें: javadude.com/articles/passbyvalue.htm
adhg

1
@ADg आपने लिखा "ऑब्जेक्ट्स को संदर्भ द्वारा पारित किया जाता है जो मूल्य से गुजरता है।" <--- वह जिबरिश है। आपका मतलब है कि वस्तुओं के संदर्भ हैं जो मूल्य द्वारा पारित किए जाते हैं
बार्लोप

2
-1 आपने लिखा "वस्तुओं को संदर्भ से पारित किया जाता है," <- FALSE। यदि यह सच था तो किसी विधि को कॉल करने और एक चर को पार करने के दौरान, विधि एक भिन्न वस्तु को चर बिंदु / संदर्भ बना सकती है, लेकिन यह नहीं हो सकता। सब कुछ जावा में मूल्य द्वारा पारित किया जाता है। और आप अभी भी किसी वस्तु की विशेषताओं को विधि के बाहर बदलने का प्रभाव देखते हैं।
बार्लोप

यहां देखें जवाब stackoverflow.com/questions/40480/…
barlop

7

जावा में सभी तर्क मूल्य द्वारा पारित किए जाते हैं। जब आप Stringकिसी फ़ंक्शन को पास करते हैं, तो जो मान पास होता है है वह स्ट्रिंग ऑब्जेक्ट का संदर्भ है, लेकिन आप उस संदर्भ को संशोधित नहीं कर सकते हैं, और अंतर्निहित स्ट्रिंग ऑब्जेक्ट अपरिवर्तनीय है।

काम

zText += foo;

के बराबर है:

zText = new String(zText + "foo");

यही है, यह (स्थानीय रूप से) zTextएक नए संदर्भ के रूप में पैरामीटर को पुन: असाइन करता है , जो एक नए मेमोरी लोकेशन की ओर इशारा करता है, जिसमें एक नया Stringहै जिसमें संलग्न की मूल सामग्री zTextहोती "foo"है।

मूल ऑब्जेक्ट को संशोधित नहीं किया गया है, और main()विधि का स्थानीय चर zTextअभी भी मूल (खाली) स्ट्रिंग को इंगित करता है।

class StringFiller {
  static void fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

प्रिंट:

Original value:
Local value: foo
Final value:

यदि आप स्ट्रिंग को संशोधित करना चाहते हैं, तो आप नोट किए गए उपयोग के रूप में StringBuilderया फिर कुछ कंटेनर (एक सरणी AtomicReferenceया एक कस्टम कंटेनर क्लास) का उल्लेख कर सकते हैं जो आपको सूचक स्तर का एक अतिरिक्त स्तर प्रदान करता है। वैकल्पिक रूप से, बस नया मान लौटाएँ और इसे असाइन करें:

class StringFiller2 {
  static String fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
    return zText;
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    zText = fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

प्रिंट:

Original value:
Local value: foo
Final value: foo

यह संभवतः सामान्य मामले में सबसे जावा-जैसा समाधान है - प्रभावी जावा आइटम "अनुकूल अपरिवर्तनीयता" देखें।

जैसा कि उल्लेख किया गया है, हालांकि, StringBuilderअक्सर आपको बेहतर प्रदर्शन देगा - यदि आपके पास करने के लिए बहुत कुछ है, खासकर एक लूप के अंदर, उपयोग करें StringBuilder

लेकिन यदि आप कर सकते हैं, तो आप के Stringsबजाय अपरिवर्तनीय के आसपास से गुजरने की कोशिश करें StringBuilders- आपका कोड पढ़ने में आसान होगा और अधिक रखरखाव योग्य होगा। अपने पैरामीटर बनाने पर विचार करें final, और जब आप किसी नए मान के लिए विधि पैरामीटर को पुन: असाइन करते हैं, तो आपको चेतावनी देने के लिए अपने IDE को कॉन्फ़िगर करें।


6
आप "सख्ती से बोल" कहते हैं जैसे कि यह लगभग अप्रासंगिक है - जबकि यह समस्या के दिल में है। यदि जावा वास्तव में संदर्भ से गुजरता है, तो यह एक समस्या नहीं होगी। कृपया "संदर्भ द्वारा जावा पास ऑब्जेक्ट्स" के मिथक के प्रचार से बचने की कोशिश करें - (सही) समझाते हुए "संदर्भ मूल्य द्वारा पारित किए जाते हैं" मॉडल बहुत अधिक उपयोगी है, आईएमओ।
जॉन स्कीट

अरे, मेरी पिछली टिप्पणी कहां गई? :-) जैसा कि मैंने पहले कहा था, और जैसा कि जॉन ने अब जोड़ा है, यहां असली मुद्दा यह है कि संदर्भ मूल्य द्वारा पारित किया गया है। यह बहुत महत्वपूर्ण है और बहुत से लोग शब्दार्थ अंतर को नहीं समझते हैं।
एड एस।

1
काफी उचित। एक जावा प्रोग्रामर के रूप में मैंने वास्तव में एक दशक से बेहतर संदर्भ में पारित कुछ भी नहीं देखा है , इसलिए मेरी भाषा सुस्त हो गई है। लेकिन आप सही हैं, मुझे अधिक ध्यान रखना चाहिए, खासकर सी-प्रोग्रामिंग दर्शकों के लिए।
डेविड मोल्स

5

स्ट्रिंग जावा में एक अपरिवर्तनीय वस्तु है। आप जिस प्रकार से कार्य करने की कोशिश कर रहे हैं, उसे करने के लिए आप StringBuilder वर्ग का उपयोग कर सकते हैं:

public static void main(String[] args)
{
    StringBuilder sb = new StringBuilder("hello, world!");
    System.out.println(sb);
    foo(sb);
    System.out.println(sb);

}

public static void foo(StringBuilder str)
{
    str.delete(0, str.length());
    str.append("String has been modified");
}

एक अन्य विकल्प स्ट्रिंग के साथ एक वर्ग बनाने के लिए एक गुंजाइश चर (अत्यधिक हतोत्साहित) के रूप में निम्नानुसार है:

class MyString
{
    public String value;
}

public static void main(String[] args)
{
    MyString ms = new MyString();
    ms.value = "Hello, World!";

}

public static void foo(MyString str)
{
    str.value = "String has been modified";
}

1
स्ट्रिंग जावा में एक आदिम प्रकार नहीं है।
20

सच है, लेकिन क्या आप वास्तव में मेरा ध्यान खींचने की कोशिश कर रहे हैं?
फदी हन्ना अल-कस

जब से जावा स्ट्रिंग एक आदिम है ?! इसलिए! स्ट्रिंग संदर्भ द्वारा पारित।
thedp

2
'अपरिवर्तनीय वस्तु' वह है जो मैं कहना चाहता था। मैंने किसी कारणवश @VWeber द्वारा टिप्पणी करने के बाद मेरे उत्तर को दोबारा नहीं पढ़ा, लेकिन अब मैं देखता हूं कि वह क्या कहना चाह रहा था। मेरी गलती। उत्तर संशोधित
फदी हन्ना अल-कस

2

उत्तर सीधा है। जावा में तार अपरिवर्तनीय हैं। इसलिए इसकी तरह 'अंतिम' संशोधक (या सी / सी ++ में 'कास्ट') का उपयोग करना। इसलिए, एक बार असाइन किए जाने के बाद, आप इसे वैसे नहीं बदल सकते जैसे आपने किया था।

आप स्ट्रिंग के किस मान को बदल सकते हैं , लेकिन आप उस वास्तविक मान को नहीं बदल सकते हैं, जिस पर वर्तमान में यह स्ट्रिंग इंगित कर रही है।

अर्थात। String s1 = "hey"। आप बना सकते हैं s1 = "woah", और यह पूरी तरह से ठीक है, लेकिन आप वास्तव में स्ट्रिंग के अंतर्निहित मूल्य को नहीं बदल सकते हैं (इस मामले में: "हे") प्लसईक्वाल्स, आदि (यानी। s1 += " whatup != "hey whatup") का उपयोग करते हुए एक बार कुछ और सौंपा जाए ।

ऐसा करने के लिए, स्ट्रिंग को वापस करने के लिए StringBuilder या StringBuffer वर्गों या अन्य उत्परिवर्तित कंटेनरों का उपयोग करें, फिर बस .toString () को कॉल करें।

नोट: स्ट्रिंग्स को अक्सर हैश कीज़ के रूप में उपयोग किया जाता है इसलिए यह इस कारण का हिस्सा है कि वे अपरिवर्तनीय क्यों हैं।


चाहे वह परिवर्तनशील हो या अपरिवर्तनीय प्रासंगिक नहीं है। =एक संदर्भ पर, कभी भी उस वस्तु को प्रभावित करता है जो कक्षा की परवाह किए बिना इंगित करता था।
नवांश

2

स्ट्रिंग जावा में एक विशेष वर्ग है। यह थ्रेड सेफ है जिसका अर्थ है "एक बार स्ट्रिंग उदाहरण बन जाने के बाद, स्ट्रिंग इंस्टेंस की सामग्री कभी नहीं बदलेगी"।

यहाँ क्या है के लिए चल रहा है

 zText += "foo";

सबसे पहले, Java कंपाइलर को ZText String इंस्टेंस का मान मिलेगा, फिर एक नया String इंस्टेंस बनाएं जिसका मान zText "foo" को जोड़ रहा हो। तो आप जानते हैं कि उस zText बिंदु का उदाहरण क्यों नहीं बदलता है। यह पूरी तरह से एक नया उदाहरण है। वास्तव में, यहां तक ​​कि स्ट्रिंग "फू" एक नया स्ट्रिंग उदाहरण है। तो, इस कथन के लिए, जावा दो स्ट्रिंग उदाहरण बनाएगा, एक "फू" है, दूसरा zText परिशिष्ट "फू" का मूल्य है। नियम सरल है: स्ट्रिंग उदाहरण का मान कभी नहीं बदला जाएगा।

विधि भरण के लिए, आप पैरामीटर के रूप में स्ट्रिंग स्ट्रिंग का उपयोग कर सकते हैं, या आप इसे इस तरह बदल सकते हैं:

String fillString(String zText) {
    return zText += "foo";
}

... हालाँकि यदि आप इस कोड के अनुसार 'fillString' को परिभाषित करते हैं, तो आपको वास्तव में इसे अधिक उपयुक्त नाम देना चाहिए!
स्टीफन C

हाँ। इस विधि को "appendString" या तो नाम दिया जाना चाहिए।
दीपनाइटटोव


1

यह कार्य StringBuffer का उपयोग करता है

public class test {
 public static void main(String[] args) {
 StringBuffer zText = new StringBuffer("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuffer zText) {
    zText .append("foo");
}
}

और भी बेहतर StringBuilder का उपयोग करें

public class test {
 public static void main(String[] args) {
 StringBuilder zText = new StringBuilder("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuilder zText) {
    zText .append("foo");
}
}

1

स्ट्रिंग जावा में अपरिवर्तनीय है। आप एक मौजूदा स्ट्रिंग शाब्दिक / वस्तु को संशोधित / बदल नहीं सकते हैं।

स्ट्रिंग s = "हैलो"; एस = s + "हाय";

यहां पिछले संदर्भ s को "HelloHi" मान की ओर इशारा करते हुए नए रेफ़रेंस s द्वारा प्रतिस्थापित किया गया है।

हालांकि, परिवर्तनशीलता लाने के लिए हमारे पास StringBuilder और StringBuffer हैं।

StringBuilder s = new StringBuilder (); s.append ( "हाय");

यह नए मान "हाय" को उसी रेफ़रेंस s में जोड़ता है। //


0

हारून दिगुल्ला के पास अब तक का सबसे अच्छा जवाब है। उनके दूसरे विकल्प का एक रूप है कम्पर लैंग लाइब्रेरी संस्करण 3+ के रैपर या कंटेनर क्लास MutableObject का उपयोग करना:

void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }

आप कंटेनर वर्ग की घोषणा को बचाते हैं। दोष कमोन लैंग लिब की निर्भरता है। लेकिन यह काम काफी उपयोगी है और लगभग किसी भी बड़ी परियोजना का मैंने इस पर काम किया है।


0

जावा में संदर्भ द्वारा एक वस्तु (स्ट्रिंग सहित) पारित करने के लिए, आप इसे एक आस-पास के एडाप्टर के सदस्य के रूप में पारित कर सकते हैं। जेनेरिक के साथ एक समाधान यहाँ है:

import java.io.Serializable;

public class ByRef<T extends Object> implements Serializable
{
    private static final long serialVersionUID = 6310102145974374589L;

    T v;

    public ByRef(T v)
    {
        this.v = v;
    }

    public ByRef()
    {
        v = null;
    }

    public void set(T nv)
    {
        v = nv;
    }

    public T get()
    {
        return v;
    }

// ------------------------------------------------------------------

    static void fillString(ByRef<String> zText)
    {
        zText.set(zText.get() + "foo");
    }

    public static void main(String args[])
    {
        final ByRef<String> zText = new ByRef<String>(new String(""));
        fillString(zText);
        System.out.println(zText.get());
    }
}

0

किसी ऐसे व्यक्ति के लिए जो अधिक उत्सुक हैं

class Testt {
    static void Display(String s , String varname){
        System.out.println(varname + " variable data = "+ s + " :: address hashmap =  " + s.hashCode());
    }

    static void changeto(String s , String t){
        System.out.println("entered function");
        Display(s , "s");
        s = t ;
        Display(s,"s");
        System.out.println("exiting function");
    }

    public static void main(String args[]){
        String s =  "hi" ;
        Display(s,"s");
        changeto(s,"bye");
        Display(s,"s");
    }
}

अब इस उपरोक्त कोड को चलाकर आप देख सकते हैं कि hashcodesस्ट्रिंग चर s के साथ पता कैसे बदल सकता है । changetoजब परिवर्तन किया जाता है तो एक नई वस्तु को वेरिएबल एस को फ़ंक्शन में आवंटित किया जाता है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.