जावा 14 रिकॉर्ड और सरणियाँ


11

निम्नलिखित कोड दिया गया है:

public static void main(String[] args) {
    record Foo(int[] ints){}

    var ints = new int[]{1, 2};
    var foo = new Foo(ints);
    System.out.println(foo); // Foo[ints=[I@6433a2]
    System.out.println(new Foo(new int[]{1,2}).equals(new Foo(new int[]{1,2}))); // false
    System.out.println(new Foo(ints).equals(new Foo(ints))); //true
    System.out.println(foo.equals(foo)); // true
}

ऐसा लगता है, जाहिर है, कि सरणी की toString, equalsविधियों का उपयोग किया जाता है (स्थिर विधियों के बजाय Arrays::equals, Arrays::deepEquals या Array::toString)।

इसलिए मुझे लगता है कि जावा 14 रिकॉर्ड्स ( जेईपी 359 ) सरणियों के साथ बहुत अच्छी तरह से काम नहीं करते हैं, संबंधित तरीकों को एक आईडीई (जो कम से कम इंटेलीज में उत्पन्न होता है, डिफ़ॉल्ट रूप से "उपयोगी" तरीके से उत्पन्न होता है, अर्थात वे स्थैतिक तरीकों का उपयोग करते हैं) में Arrays)।

या कोई और उपाय है?


3
कैसे Listएक सरणी के बजाय का उपयोग कर के बारे में ?
ओलेग

मुझे समझ नहीं आ रहा है कि ऐसे तरीकों को आईडीई के साथ क्यों उत्पन्न किया जाना चाहिए? सभी तरीकों को हाथ से उत्पन्न करने में सक्षम होना चाहिए।
खानाबदोश

1
toString(), equals()और hashCode()एक रिकार्ड के तरीकों लागू किया जाता है एक invokedynamic संदर्भ का उपयोग करते हुए। । यदि केवल संकलित वर्ग समतुल्य हो सकता है, तो वह Arrays.deepToStringविधि जो आज उसकी निजी अतिभारित पद्धति में है, वह प्राइमिटिव मामलों के लिए हल हो सकती है।
नमन

1
कार्यान्वयन के लिए डिजाइन की पसंद के लिए, इन विधियों के एक ओवरराइड कार्यान्वयन प्रदान नहीं करने के लिए कुछ के लिए, यह वापस गिर करने के लिए एक बुरा विचार नहीं है Object, क्योंकि उपयोगकर्ता-परिभाषित कक्षाओं के साथ भी ऐसा हो सकता है। जैसे गलत बराबरी
नमन

1
@ नोमान का उपयोग करने का विकल्प invokedynamicशब्दार्थ के चयन के साथ बिल्कुल कुछ भी नहीं है; इंडी एक शुद्ध कार्यान्वयन विवरण है। कंपाइलर एक ही काम करने के लिए बायटेकोड का उत्सर्जन कर सकता था; यह वहां पहुंचने के लिए सिर्फ एक अधिक कुशल और लचीला तरीका था। रिकॉर्ड के डिजाइन के दौरान इसकी बड़े पैमाने पर चर्चा की गई थी कि क्या अधिक संतुलित समानता शब्दार्थ (जैसे सरणियों के लिए गहरी समानता) का उपयोग करना है, लेकिन यह माना जाता है कि हल करने की तुलना में अधिक समस्याओं का कारण बनता है।
ब्रायन गोएट्ज

जवाबों:


18

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

आपके उदाहरण के साथ मूल समस्या यह है कि आप चाहते हैं कि equals()सरणियों का अर्थ सामग्री समानता है, संदर्भ समानता नहीं। equals()रिकॉर्ड के लिए (डिफ़ॉल्ट) शब्दार्थ , घटकों की समानता पर आधारित है; आपके उदाहरण में, Fooअलग-अलग सरणियों वाले दो रिकॉर्ड अलग - अलग हैं, और रिकॉर्ड सही ढंग से व्यवहार कर रहा है। समस्या यह है कि आप चाहते हैं कि समानता की तुलना अलग हो।

उस ने कहा, आप जो शब्दार्थ चाहते हैं, उसके साथ एक रिकॉर्ड की घोषणा कर सकते हैं, यह सिर्फ अधिक काम लेता है, और आपको लग सकता है कि यह बहुत अधिक काम है। यहाँ एक रिकॉर्ड है कि आप क्या चाहते हैं:

record Foo(String[] ss) {
    Foo { ss = ss.clone(); }
    String[] ss() { return ss.clone(); }
    boolean equals(Object o) { 
        return o instanceof Foo 
            && Arrays.equals(((Foo) o).ss, ss);
    }
    int hashCode() { return Objects.hash(Arrays.hashCode(ss)); }
}

यह क्या करता है (कंस्ट्रक्टर में) और बाहर (एक्सेसर में) रास्ते में एक रक्षात्मक प्रतिलिपि, साथ ही साथ सरणी की सामग्री का उपयोग करने के लिए समानता शब्दार्थ को समायोजित करना। यह सुपरक्लास में आवश्यक अपरिवर्तनीय का समर्थन करता है java.lang.Record, जो "अपने घटकों में एक रिकॉर्ड को अलग करता है, और घटकों को एक नए रिकॉर्ड में फिर से संगठित करता है, एक समान रिकॉर्ड प्राप्त करता है।"

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


6
साथ ही, यह उन लोगों द्वारा की जाने वाली एक सामान्य गलती है जो चाहते हैं कि सरणी समानता सामग्री द्वारा यह मान लिया जाए कि कोई भी कभी भी संदर्भ द्वारा सरणी समानता नहीं चाहता है। लेकिन यह सच नहीं है; यह सिर्फ इतना है कि कोई भी जवाब नहीं है जो सभी मामलों के लिए काम करता है। कभी-कभी संदर्भ समानता वास्तव में आप क्या चाहते हैं।
ब्रायन गोएट्ज

9

List< Integer > वैकल्पिक हल

वर्कअराउंड: आदिमों की सरणी ( ) के बजाय ऑब्जेक्ट्स ( ) Listका उपयोग करें ।IntegerList< Integer >int[]

इस उदाहरण में, मैं जावा 9 में जोड़े गए फ़ीचर का उपयोग करके अनिर्दिष्ट वर्ग की एक अपरिवर्तनीय सूची को तुरंत समाप्त कर देता हूं। List.ofआप ArrayListएक सरणी द्वारा समर्थित एक परिवर्तनीय सूची के लिए बस उपयोग कर सकते हैं ।

package work.basil.example;

import java.util.List;

public class RecordsDemo
{
    public static void main ( String[] args )
    {
        RecordsDemo app = new RecordsDemo();
        app.doIt();
    }

    private void doIt ( )
    {

        record Foo(List < Integer >integers)
        {
        }

        List< Integer > integers = List.of( 1 , 2 );
        var foo = new Foo( integers );

        System.out.println( foo ); // Foo[integers=[1, 2]]
        System.out.println( new Foo( List.of( 1 , 2 ) ).equals( new Foo( List.of( 1 , 2 ) ) ) ); // true
        System.out.println( new Foo( integers ).equals( new Foo( integers ) ) ); // true
        System.out.println( foo.equals( foo ) ); // true
    }
}

यह हमेशा एक अच्छा विचार नहीं है, हालांकि एक सरणी पर सूची का चयन करना और हम वैसे भी इस पर चर्चा
नमन

@ नोमान मैंने कभी भी "अच्छा विचार" होने के बारे में दावा नहीं किया। प्रश्न का शाब्दिक रूप से अन्य समाधानों के लिए कहा गया है। मैंने एक प्रदान किया। और मैंने आपके द्वारा लिंक की गई टिप्पणियों से एक घंटे पहले किया था।
बेसिल बॉर्क

5

वर्कअराउंड: एकIntArrayक्लासबनाएंऔर रैप करेंint[]

record Foo(IntArray ints) {
    public Foo(int... ints) { this(new IntArray(ints)); }
    public int[] getInts() { return this.ints.get(); }
}

सही नहीं है, क्योंकि आपको अब foo.getInts()इसके बजाय कॉल करना है foo.ints(), लेकिन बाकी सब कुछ आपके इच्छित तरीके से काम करता है।

public final class IntArray {
    private final int[] array;
    public IntArray(int[] array) {
        this.array = Objects.requireNonNull(array);
    }
    public int[] get() {
        return this.array;
    }
    @Override
    public int hashCode() {
        return Arrays.hashCode(this.array);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null || getClass() != obj.getClass())
            return false;
        IntArray that = (IntArray) obj;
        return Arrays.equals(this.array, that.array);
    }
    @Override
    public String toString() {
        return Arrays.toString(this.array);
    }
}

उत्पादन

Foo[ints=[1, 2]]
true
true
true

1
क्या यह एक वर्ग का उपयोग करने के लिए कहने के बराबर नहीं है और ऐसे मामले में रिकॉर्ड नहीं है?
नमन

1
@ नोमान बिल्कुल नहीं, क्योंकि आपके recordकई क्षेत्रों से मिलकर बना हो सकता है, और केवल सरणी फ़ील्ड (ओं) को इस तरह से लपेटा जाता है।
एंड्रियास

मुझे वह बिंदु मिलता है, जिसे आप पुन: प्रयोज्यता के संदर्भ में बनाने की कोशिश कर रहे हैं, लेकिन फिर इनबिल्ट कक्षाएं भी हैं, Listजो इस तरह का आवरण प्रदान करती हैं, जैसा कि यहां प्रस्तावित समाधान के साथ हो सकता है। या क्या आप मानते हैं कि ऐसे उपयोग मामलों के लिए एक उपरि हो सकता है?
नमन

3
@ नोमान यदि आप एक स्टोर करना चाहते हैं int[], तो List<Integer>कम से कम दो कारणों से एक समान नहीं है, 1) सूची में बहुत अधिक मेमोरी का उपयोग किया गया है, और 2) उनके बीच कोई अंतर्निर्मित रूपांतरण नहीं है। Integer[]🡘 List<Integer>आसान काफी है ( toArray(...)और Arrays.asList(...)), लेकिन int[]🡘 List<Integer>अधिक काम लेता है, और रूपांतरण में समय लगता है, तो यह चलो कुछ आप हर समय ऐसा करने के लिए नहीं करना चाहती। यदि आपके पास एक है int[]और इसे एक रिकॉर्ड में संग्रहीत करना चाहते हैं (अन्य सामान के साथ, अन्यथा एक रिकॉर्ड का उपयोग क्यों करें), और इसे एक के रूप में आवश्यकता है int[], तो हर बार जब आपको इसकी आवश्यकता होती है तब इसे परिवर्तित करना गलत है।
एंड्रियास

वास्तव में अच्छी बात है। मैं हालांकि अगर आप की अनुमति देने के nitpicking पूछ सकता है क्या लाभ हम अभी भी साथ देख पा रहे हैं Arrays.equals, Arrays.toStringसे अधिक Listहै, जबकि जगह इस्तेमाल किया कार्यान्वयन int[]के रूप में अन्य जवाब में सुझाव दिया। (मान लें कि ये दोनों वैसे भी वर्कअराउंड हैं।)
नमन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.