मैं एक ऐसी विधि लिखने की कोशिश कर रहा हूं जो नेस्टेड होने पर एक सामान्य सरणी को फ़्लैट कर देगी।

    private static <T> List<T> flatten(T[] in) {
        List<T> result = new ArrayList<>();
        for (T e : in) {
            if (e.getClass().isArray()) {
                result.addAll(Arrays.asList(e)); ## Issue is here. 
            } else {
                result.add(e);
            }
        }
        return result;
    }

यह कोड किसी भी त्रुटि का कारण नहीं बनता है लेकिन यह भी काम नहीं करता है। जब e एक सरणी नहीं है, चीजें अपेक्षा के अनुरूप काम करती हैं... एक सूची in के तत्वों से भर जाती है और वापस आ जाती है।

हालांकि जब e.getClass().isArray() == true, e के अवयव नहीं जोड़े जाते हैं। बल्कि मूल सरणी जोड़ी जाती है इसलिए मैं सरणियों की एक सूची के साथ समाप्त होता हूं।

यहां मेरा उपयोग मामला यह है कि मेरे पास एक विधि है जिसे जेनरिक पारित किया जा रहा है T[] someArray

public <T> void doSomeStuff(T[] someArray) {
   Set<T> unique = Sets.newHashSet(someArray)
   ... do some stuff with the unique values ... 
}

इनपुट someArray या तो नेस्टेड हो सकता है या नहीं (यानी T स्वयं एक सरणी हो सकता है, जिसके परिणामस्वरूप T[][])। मैं इनपुट में निहित अद्वितीय तत्वों को निर्धारित करना चाहता हूं, चाहे वह नेस्टेड हो या नहीं। इनपुट someArray को एक सेट में पास करना केवल तभी काम करता है जब यह नेस्टेड न हो, इसलिए मैं समतल करने की कोशिश कर रहा हूं।

तो मेरा सवाल है, मैं यह कैसे कर सकता हूं और मेरी विधि ऊपर क्यों काम नहीं कर रही है? शिक्षा के लिए अग्रिम धन्यवाद।

2
rocksNwaves 17 जुलाई 2021, 21:21
जेनेरिक मिटा दिए जाते हैं, इसलिए सामान्य प्रकार की जानकारी अब नहीं रह गई है रनटाइम पर उपलब्ध है। तथ्य यह है कि जेनरिक अपरिवर्तनीय हैं और मिट जाते हैं जबकि सरणियाँ सहसंयोजक और बरकरार रहती हैं, जब वे मिश्रित होती हैं तो परेशानी का एक नुस्खा है। ठोस मामले में, स्थिर प्रकार का e (जो T है) नामक विधि के हस्ताक्षर को निर्धारित करता है, और यह Arrays.asList(T...).
 – 
Turing85
17 जुलाई 2021, 21:26
मैंने उस पृष्ठ को पहले पढ़ा है, और ईमानदारी से इसे समझ में नहीं आता। क्या आसपास कोई काम है?
 – 
rocksNwaves
17 जुलाई 2021, 21:31
यह एक भाषा सीमा है। अभी तक, इस समस्या का कोई (सरल) समाधान नहीं है।
 – 
Turing85
17 जुलाई 2021, 21:31

1 उत्तर

सबसे बढ़िया उत्तर

आपका कोड काम नहीं कर सकता। जेनरिक बस लाइन अप नहीं करते हैं।

मान लें कि आपके पास एक सरणी है जो स्ट्रिंग्स और स्ट्रिंग्स के सरणी का संयोजन है। यह संभवतः तब तक T[] नहीं हो सकता जब तक कि T वस्तु न हो, जो आप नहीं चाहते (जैसा कि इसका मतलब होगा कि आपको एक List<Object> मिलेगा। आखिरकार, यदि T String है, तो आपकी इनपुट सरणी, जिसे T[] in के रूप में परिभाषित किया गया है, एक String[] in है, जिसमें स्ट्रिंग सरणी नहीं हो सकती। आखिरकार, एक स्पष्ट कारणों से String[] String का उपप्रकार नहीं है।

जेनेरिक के संदर्भ में 'स्ट्रिंग्स की एक सरणी, या स्ट्रिंग्स की सरणी की सरणी, या स्ट्रिंग्स के सरणी के सरणी की सरणी, और आगे' की अवधारणा का वर्णन करना असंभव है। इसलिए जेनरिक का यहां कोई स्थान नहीं है। यदि आप इसे चाहते हैं, तो आप केवल 'टाइप' कर सकते हैं 'एक सरणी जिसका घटक प्रकार अज्ञात और हाइब्रिड है', जो कि जावा में Object[] है (यह सह/विपरीत-भिन्नता के अनुसार टूटा हुआ है, लेकिन यह सिर्फ एक हिस्सा है जावा स्पेक का: सरणी पर भिन्नता गलत है, ज्ञात समस्या है और ठीक करने योग्य नहीं है)।

यह आपको एक माध्यमिक मुद्दा देता है: जेनेरिक मिटा दिए जाते हैं, और उस मॉडल में आपके पास काम करने के लिए वास्तविक प्रकार नहीं होता है। वास्तव में, क्योंकि इनपुट सरणी पर कुछ प्रकार की जाँच करने के लिए संकलक को बताने के लिए जेनरिक का उपयोग करना असंभव है, संकलक आपके लिए कुछ भी नहीं कर सकता है, इसलिए किसी भी प्रकार की जाँच आप चाहते हैं (और आप स्पष्ट रूप से चाहते हैं, आप नहीं करते हैं) t एक List of who knows what this is वापस करना चाहते हैं), रनटाइम पर करना होगा।

दुर्भाग्य से, ऐसा करना भी असंभव है - आप यह जांच नहीं सकते कि रनटाइम के दौरान कोई वस्तु है, मान लीजिए, Map<String, Integer>

तो, आप जो चाहते हैं वह असंभव है।

यह संभव हो जाता है यदि आप इस पद्धति के साथ ठीक हैं तो केवल संशोधित प्रकारों के लिए काम करने में सक्षम हैं। अर्थात्, वे प्रकार जिनमें स्वयं कोई <> नहीं है। इसलिए, यदि आप "एक सरणी लेना चाहते हैं जिसमें" स्ट्रिंग का मानचित्र "पूर्णांक तक" और "स्ट्रिंग के मानचित्रों के पूर्णांक से पूर्णांक" का संयोजन हो, तो यह विधि ऐसा करने में सक्षम नहीं होगी और यह वास्तव में पूरी तरह से असंभव है जावा में ऐसा करने के लिए। लेकिन अगर आप ठीक हैं, तो कहें, "एक सरणी जिसमें स्ट्रिंग्स और स्ट्रिंग्स के सरणी का संयोजन होता है" और इसे स्ट्रिंग्स की एक फ़्लैट-आउट सूची में बदलना चाहते हैं, ठीक है, यह संभव है।

यह जटिल है, यद्यपि:

public <T> List<T> flattenArray(Class<T> type, Object[] in) {
    if (type.isArray()) throw new IllegalArgumentException();
    var out = new ArrayList<T>();
    flattenArray0(type, in, out);
    return out;
}

private <T> void flattenArray0(Class<T> type, Object[] in, List<T> out) {
    for (Object a : in) {
        if (a == null) {
            out.add(null);
        } else if (a.getClass().isArray()) {
            flattenArray0(type, (Object[]) a, out);
        } else {
            out.add(type.cast(a));
        }
    }
}

कार्रवाई में:

Object[] test = new Object[3];
test[0] = "Hello";
test[1] = new String[] {"Foo", "Bar"};
Object[] threeDeep = new Object[2];
test[2] = threeDeep;
threeDeep[0] = "Goodbye";
threeDeep[1] = new String[] {"Baz"};

List<String> result = flattenArray(String.class, test);
System.out.println(result);

प्रिंट करना चाहिए: ["Hello", "Foo", "Bar", "Goodbye", "Baz"]

4
rzwitserloot 17 जुलाई 2021, 21:34
हाँ, मैं निश्चित रूप से प्रकार की जाँच करना चाहता हूँ और वस्तु प्रकारों को मिलाने की कोशिश नहीं कर रहा हूँ। मैं बस उसी विधि से 1 या 2D सरणियों पर काम करने में सक्षम होना चाहता था।
 – 
rocksNwaves
17 जुलाई 2021, 21:42
उस सीमा के साथ भी आप इसे पूरा नहीं कर सकते - जावा में या तो String[] या String[][] को एक ही प्रकार के रूप में व्यक्त करने का कोई तरीका नहीं है, भले ही आप कोशिश करें जेनरिक को शामिल करना।
 – 
rzwitserloot
17 जुलाई 2021, 22:27