String.chars () जावा 8 में ints की एक धारा क्यों है?


197

जावा 8 में, एक नई विधि है String.chars()जो intएस की एक धारा ( IntStream) लौटाती है जो चरित्र कोड का प्रतिनिधित्व करती है। मुझे लगता है कि कई लोग charइसके बजाय यहाँ की एक धारा की उम्मीद करेंगे । एपीआई को इस तरह से डिजाइन करने की प्रेरणा क्या थी?


4
@ रोहितजैन मेरा मतलब किसी विशेष धारा से नहीं था। यदि CharStreamमौजूद नहीं है, तो इसे जोड़ने में क्या समस्या होगी?
एडम डाइगा

5
@ अदम्यगा: आदिम धाराओं को सीमित करने के लिए डिजाइनरों ने विस्फोटक तरीके से 3 प्रकारों को सीमित करने के लिए चुना, क्योंकि अन्य प्रकार (चार, लघु, फ्लोट) को उनके बड़े समकक्ष (इंट, डबल) द्वारा किसी भी महत्वपूर्ण के बिना दर्शाया जा सकता है। प्रदर्शन जुर्माना।
जेबी निज़ेट

3
@ जेबीएनज़ेट मुझे मिल गया। लेकिन यह अभी भी एक गंदे समाधान की तरह महसूस करता है, केवल कुछ नए वर्गों को बचाने के लिए।
एडम डायगा

9
@ जेबी निज़ेट: मेरे लिए ऐसा लग रहा है कि हमारे पास पहले से ही इंटरफेरेंस का एक धमाका है जो सभी स्ट्रीम को ओवरलोड करने के साथ-साथ सभी फंक्शन इंटरफेस को भी दिया है ...
होल्गर

5
हां, पहले से ही एक विस्फोट है, यहां तक ​​कि केवल तीन आदिम धारा विशेषज्ञताओं के साथ। यदि सभी आठों प्राइमरी में विशेषज्ञता हो, तो क्या होगा? एक प्रलय? :-)
स्टुअर्ट मार्क्स

जवाबों:


217

जैसा कि दूसरों ने पहले ही उल्लेख किया है, इसके पीछे डिजाइन का निर्णय तरीकों और कक्षाओं के विस्फोट को रोकने के लिए था।

फिर भी, व्यक्तिगत रूप से मुझे लगता है कि यह एक बहुत ही बुरा निर्णय था, और वहाँ, वे नहीं करना चाहते हैं CharStream, जो उचित है, के बजाय विभिन्न तरीकों chars(), मैं के बारे में सोचना होगा:

  • Stream<Character> chars(), जो बक्से के पात्रों की एक धारा देता है, जिसमें कुछ प्रकाश प्रदर्शन जुर्माना होगा।
  • IntStream unboxedChars(), जो प्रदर्शन कोड के लिए उपयोग किया जाएगा।

हालांकि , इस पर ध्यान केंद्रित करने के बजाय कि यह वर्तमान में इस तरह क्यों किया गया है, मुझे लगता है कि इस उत्तर को एपीआई के साथ ऐसा करने का तरीका दिखाने पर ध्यान केंद्रित करना चाहिए जिसे हमने जावा 8 के साथ प्राप्त किया है।

जावा 7 में मैंने इसे इस तरह किया होगा:

for (int i = 0; i < hello.length(); i++) {
    System.out.println(hello.charAt(i));
}

और मुझे लगता है कि यह जावा 8 में करने के लिए एक उचित तरीका निम्नलिखित है:

hello.chars()
        .mapToObj(i -> (char)i)
        .forEach(System.out::println);

यहां मैं IntStreamएक वस्तु प्राप्त करता हूं और इसे लंबोदा के माध्यम से किसी ऑब्जेक्ट पर मैप करता हूं i -> (char)i, यह स्वचालित रूप से इसे एक बॉक्स में रखेगा Stream<Character>, और फिर हम वह कर सकते हैं जो हम चाहते हैं, और अभी भी प्लस के रूप में विधि संदर्भ का उपयोग करते हैं।

इस बात से अवगत रहें कि आपको क्या करना चाहिएmapToObj , अगर आप भूल जाते हैं और उपयोग करते हैं map, तो कुछ भी शिकायत नहीं करेगा, लेकिन आप अभी भी एक के साथ समाप्त IntStreamहो जाएंगे, और आपको यह सोचकर छोड़ दिया जा सकता है कि यह वर्णों का प्रतिनिधित्व करने वाले तार के बजाय पूर्णांक मानों को क्यों प्रिंट करता है।

जावा 8 के लिए अन्य बदसूरत विकल्प:

एक शेष में IntStreamऔर उन्हें अंततः मुद्रित करना चाहते हैं, आप मुद्रण के लिए अब विधि संदर्भों का उपयोग नहीं कर सकते हैं:

hello.chars()
        .forEach(i -> System.out.println((char)i));

इसके अलावा, अपनी खुद की विधि के लिए विधि संदर्भ का उपयोग अब और काम नहीं करता है! निम्नलिखित को धयान मे रखते हुए:

private void print(char c) {
    System.out.println(c);
}

और फिर

hello.chars()
        .forEach(this::print);

यह एक संकलित त्रुटि देगा, क्योंकि संभवतः एक हानिपूर्ण रूपांतरण है।

निष्कर्ष:

एपीआई क्योंकि जोड़ने के लिए इच्छुक नहीं की इस तरह से डिजाइन किया गया था CharStream, मैं व्यक्तिगत रूप से लगता है कि विधि एक लौटना चाहिए Stream<Character>, और वैकल्पिक हल वर्तमान में उपयोग करने के लिए है mapToObj(i -> (char)i)एक पर IntStreamउन लोगों के साथ ठीक से काम करने में सक्षम हो।


7
मेरा निष्कर्ष: एपीआई का यह हिस्सा डिजाइन से टूट गया है। लेकिन व्यापक उत्तर के लिए धन्यवाद
एडम डायगा

27
+1, लेकिन मेरा प्रस्ताव codePoints()इसके बजाय उपयोग करना है chars()और आपको बहुत सारे पुस्तकालय कार्य पहले से ही intकोड पॉइंट के लिए स्वीकार करने होंगे char, जैसे कि सभी विधियों के java.lang.Characterसाथ-साथ StringBuilder.appendCodePoint, आदि jdk1.5। तब से यह समर्थन मौजूद है ।
होल्गर

6
कोड बिंदुओं के बारे में अच्छी बात। का उपयोग करते हुए उन्हें अनुपूरक वर्ण है, जो एक में किराए की जोड़े के रूप में प्रतिनिधित्व कर रहे हैं संभाल लेंगे Stringया char[]। मैं शर्त लगाता हूं कि अधिकांश charप्रोसेसिंग कोड गलत जोड़े को जोड़ते हैं।
स्टुअर्ट मार्क्स

2
@skiwi, परिभाषित करें void print(int ch) { System.out.println((char)ch); }और फिर आप विधि संदर्भों का उपयोग कर सकते हैं।
स्टुअर्ट मार्क्स

2
Stream<Character>अस्वीकार किए जाने के लिए मेरा जवाब देखें ।
स्टुअर्ट मार्क्स

90

Skiwi से जवाब पहले से ही प्रमुख अंक के कई कवर किया। मैं थोड़ा और बैकग्राउंड भर दूंगा।

किसी भी एपीआई का डिज़ाइन ट्रेडऑफ़ की एक श्रृंखला है। जावा में, मुश्किल मुद्दों में से एक डिजाइन निर्णयों के साथ काम कर रहा है जो बहुत पहले किए गए थे।

1.0 से आदिम जावा में हैं। वे जावा को एक "अशुद्ध" वस्तु-उन्मुख भाषा बनाते हैं, क्योंकि आदिम वस्तुएं नहीं हैं। प्राइमिटिव्स के अलावा, मेरा मानना ​​है, वस्तु-उन्मुख शुद्धता की कीमत पर प्रदर्शन में सुधार करने का एक व्यावहारिक निर्णय था।

यह एक ट्रेडऑफ़ है जिसे हम आज भी जी रहे हैं, लगभग 20 साल बाद। जावा 5 में जोड़ा गया ऑटोबॉक्सिंग फीचर ने ज्यादातर बॉक्सिंग और अनबॉक्सिंग विधि कॉल के साथ स्रोत कोड को अव्यवस्थित करने की आवश्यकता को समाप्त कर दिया, लेकिन ओवरहेड अभी भी है। कई मामलों में यह ध्यान देने योग्य नहीं है। हालाँकि, यदि आप एक आंतरिक लूप के भीतर मुक्केबाजी या अनबॉक्सिंग करने के लिए थे, तो आप देखेंगे कि यह महत्वपूर्ण सीपीयू और कचरा संग्रहण हेडहेड लगा सकता है।

स्ट्रीम्स एपीआई को डिजाइन करते समय, यह स्पष्ट था कि हमें आदिम का समर्थन करना था। बॉक्सिंग / अनबॉक्सिंग ओवरहेड समानता से किसी भी प्रदर्शन लाभ को मार देगा। हम सभी आदिमयों का समर्थन नहीं करना चाहते थे , हालाँकि, चूंकि इसमें एपीआई में अव्यवस्था की एक बड़ी मात्रा शामिल थी। (क्या आप वास्तव में एक के लिए एक उपयोग देख सकते हैं ShortStream?) "सभी" या "कोई नहीं" एक डिजाइन के लिए आरामदायक स्थान हैं, फिर भी न तो स्वीकार्य था। इसलिए हमें "कुछ" का उचित मूल्य ढूंढना था। हम के लिए आदिम विशेषज्ञता के साथ समाप्त हो गया int, longऔर double। (व्यक्तिगत रूप से मुझे छोड़ दिया गया होगा, intलेकिन यह सिर्फ मुझे है।)

के लिए CharSequence.chars()हम वापस करने पर विचार Stream<Character>(एक प्रारंभिक प्रोटोटाइप इस को लागू किया है हो सकता है), लेकिन यह मुक्केबाजी भूमि के ऊपर की वजह से अस्वीकार कर दिया था। यह देखते हुए कि एक स्ट्रिंग charमें आदिम के रूप में मूल्य हैं, यह बिना शर्त बॉक्सिंग लगाने के लिए एक गलती प्रतीत होगी, जब कॉलर शायद मूल्य पर थोड़ा प्रसंस्करण करेगा और इसे सही तरीके से वापस एक स्ट्रिंग में अनबॉक्स करेगा।

हमने एक CharStreamआदिम विशेषज्ञता पर भी विचार किया , लेकिन इसका उपयोग एपीआई की तुलना में थोक की मात्रा की तुलना में काफी संकीर्ण प्रतीत होगा। इसे जोड़ना सार्थक नहीं लगा।

कॉल करने वालों पर यह जुर्माना यह है कि उन्हें यह जानना होगा कि IntStreamसम्‍मिलित charमूल्‍यों में सम्‍मिलित मूल्‍य सम्‍मिलित हैं intsऔर उन्‍हें उचित स्‍थान पर किया जाना चाहिए। यह दोगुना भ्रमित करने वाला है क्योंकि इसमें ओवरलोड एपीआई कॉल जैसी हैं PrintStream.print(char)और PrintStream.print(int)जो उनके व्यवहार में स्पष्ट रूप से भिन्न हैं। भ्रम का एक अतिरिक्त बिंदु संभवतः उत्पन्न होता है क्योंकि codePoints()कॉल भी वापस आ जाता है IntStreamलेकिन इसमें शामिल मान काफी भिन्न होते हैं।

इसलिए, यह कई विकल्पों में से व्यावहारिक रूप से चुनने के लिए उबलता है:

  1. हम कोई आदिम विशेषज्ञता प्रदान नहीं कर सके, जिसके परिणामस्वरूप एक सरल, सुरुचिपूर्ण, सुसंगत एपीआई, लेकिन जो उच्च प्रदर्शन और जीसी ओवरहेड लगाता है;

  2. हम एपीआई को अव्यवस्थित करने और JKK डेवलपर्स पर एक रखरखाव बोझ लगाने की कीमत पर आदिम विशेषज्ञता का एक पूरा सेट प्रदान कर सकते हैं; या

  3. हम आदिम विशेषज्ञताओं का एक सबसेट प्रदान कर सकते हैं, जो मध्यम आकार का, उच्च प्रदर्शन वाला एपीआई है जो उपयोग के मामलों (चार प्रसंस्करण) की काफी संकीर्ण सीमा में कॉलर्स पर अपेक्षाकृत छोटा बोझ लगाता है।

हमने आखिरी वाला चुना।


1
अच्छा उत्तर! हालांकि यह जवाब नहीं देता कि दो अलग-अलग विधियां क्यों नहीं हो सकती हैं chars(), एक जो Stream<Character>(छोटे प्रदर्शन के दंड के साथ) और दूसरी जा रही है IntStream, क्या यह भी माना जाता था? यह काफी संभावना है कि लोग इसे किसी Stream<Character>भी तरह से मैप करना समाप्त कर देंगे, अगर उन्हें लगता है कि प्रदर्शन के दंड को लेकर आश्वस्त है।
स्किवि

3
इसमें मिनिमलिज्म आता है। यदि पहले से ही ऐसा chars()तरीका है जो चार मानों को वापस लौटाता है IntStream, तो यह एक और एपीआई कॉल करने के लिए बहुत कुछ नहीं जोड़ता है जो समान मूल्यों को प्राप्त करता है लेकिन बॉक्सिंग रूप में। कॉलर बहुत परेशानी के बिना मूल्यों को बॉक्स कर सकता है। यकीन है कि यह (शायद दुर्लभ) मामले में ऐसा नहीं करना अधिक सुविधाजनक होगा, लेकिन एपीआई में अव्यवस्था जोड़ने की कीमत पर।
स्टुअर्ट मार्क्स

5
डुप्लिकेट प्रश्न के लिए धन्यवाद मैंने इस पर ध्यान दिया। मैं मानता हूं कि chars()वापसी IntStreamएक बड़ी समस्या नहीं है, विशेष रूप से इस तथ्य को देखते हुए कि इस पद्धति का उपयोग शायद ही कभी किया जाता है। हालांकि यह एक अंतर्निहित वापस परिवर्तित करने के लिए अच्छा होगा IntStreamकरने के लिए String। इसके साथ किया जा सकता है .reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString(), लेकिन यह वास्तव में लंबा है।
टैगिर वलेव

7
@TagirValeev हाँ यह कुछ बोझिल है। कोड पॉइंट (एक IntStream) की एक धारा के साथ यह बहुत बुरा नहीं है collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString():। मुझे लगता है कि यह वास्तव में कम नहीं है, लेकिन कोड बिंदुओं का उपयोग करने से (char)कास्ट से बचा जाता है और विधि संदर्भों के उपयोग की अनुमति मिलती है। साथ ही यह सरोगेट्स को ठीक से संभालता है।
स्टुअर्ट मार्क्स

2
@IlyaBystrov दुर्भाग्य से आदिम धाराएं IntStreamएक collect()विधि नहीं है जो एक लेती है Collector। उनके पास collect()पिछली टिप्पणियों में वर्णित केवल तीन-arg विधि है।
स्टुअर्ट मार्क्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.