java - जावा में जेनेरिक सरणी कैसे बनाएं?




arrays generics (20)

जावा जेनरिक के कार्यान्वयन के कारण, आपके पास ऐसा कोड नहीं हो सकता है:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

टाइप सुरक्षा को बनाए रखने के दौरान मैं इसे कैसे कार्यान्वित कर सकता हूं?

मैंने जावा फ़ोरम पर एक समाधान देखा जो इस तरह जाता है:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

लेकिन मुझे वास्तव में यह नहीं मिलता कि क्या हो रहा है।


अधिक आयामों तक विस्तार करने के लिए, बस [] और आयाम मानकों को newInstance() ( T एक प्रकार पैरामीटर है, newInstance() एक Class<T> , d1 से d5 पूर्णांक हैं):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

विवरण के लिए Array.newInstance() देखें।


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

हालांकि, इस निहित कलाकार ने ठीक काम किया:

Item<K>[] array = new Item[SIZE];

जहां आइटम एक वर्ग है जिसमें मैंने सदस्य को परिभाषित किया है:

private K value;

इस तरह आपको श्रेणी के प्रकार की एक सरणी मिलती है (यदि आइटम में केवल मूल्य है) या क्लास आइटम में परिभाषित कोई भी सामान्य प्रकार।


आप एक कास्ट का उपयोग कर सकते हैं:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}

आप ऑब्जेक्ट सरणी बना सकते हैं और इसे हर जगह ई पर डाल सकते हैं। हाँ, यह करने के लिए यह बहुत साफ तरीका नहीं है लेकिन इसे कम से कम काम करना चाहिए।


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

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

जैसा कि अन्य ने कहा है कि संकलन के दौरान जेनेरिक "मिटा" गए हैं। तो रनटाइम पर जेनेरिक का एक उदाहरण यह नहीं जानता कि इसका घटक प्रकार क्या है। इसका कारण ऐतिहासिक है, सूर्य मौजूदा इंटरफ़ेस (स्रोत और बाइनरी दोनों) को तोड़ने के बिना जेनेरिक जोड़ना चाहता था।

दूसरी ओर Arrays रनटाइम पर उनके घटक प्रकार पता है।

यह उदाहरण उस कोड को लेकर समस्या के आसपास काम करता है जो कन्स्ट्रक्टर को कॉल करता है (जो कि प्रकार को जानता है) कक्षा को आवश्यक प्रकार बताते हुए पैरामीटर पास करता है।

तो आवेदन कक्षा के साथ कुछ निर्माण करेगा

Stack<foo> = new Stack<foo>(foo.class,50)

और कन्स्ट्रक्टर अब जानता है (रनटाइम पर) घटक का प्रकार क्या है और प्रतिबिंब एपीआई के माध्यम से सरणी बनाने के लिए उस जानकारी का उपयोग कर सकता है।

Array.newInstance(clazz, capacity);

आखिर में हमारे पास एक प्रकार का कास्ट है क्योंकि संकलक के पास यह जानने का कोई तरीका नहीं है कि Array#newInstance() द्वारा लौटाई गई Array#newInstance() सही प्रकार है (भले ही हम जानते हों)।

यह शैली थोड़ी बदसूरत है लेकिन कभी-कभी जेनेरिक प्रकार बनाने के लिए कम से कम खराब समाधान हो सकता है जिसे किसी भी कारण से रनटाइम पर अपने घटक प्रकार को जानने की आवश्यकता होती है (सरणी बनाना, या उनके घटक प्रकार के उदाहरण बनाना आदि)।


आपको कन्स्ट्रक्टर को क्लास तर्क पास करने की आवश्यकता नहीं है। इसे इस्तेमाल करे।

static class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

तथा

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

परिणाम:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]

इस समाधान के बारे में क्या?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

यह काम करता है और सच होने के लिए बहुत आसान लग रहा है। क्या कोई कमी है?


इसे इस्तेमाल करे।

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

एक आसान, यद्यपि इसके लिए गन्दा कामकाज आपके मुख्य वर्ग के अंदर एक दूसरी "धारक" कक्षा घोंसला करना होगा, और इसका उपयोग अपने डेटा को पकड़ने के लिए करें।

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

जावा 8 में, हम लैम्ब्डा या विधि संदर्भ का उपयोग करके एक प्रकार का जेनेरिक सरणी निर्माण कर सकते हैं। यह प्रतिबिंबित दृष्टिकोण (जो Class पास करता है) के समान है, लेकिन यहां हम प्रतिबिंब का उपयोग नहीं कर रहे हैं।

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

उदाहरण के लिए, इसका उपयोग <A> A[] Stream.toArray(IntFunction<A[]>) द्वारा किया जाता है।

यह अनाम कक्षाओं का उपयोग कर प्री-जावा 8 भी किया जा सकता है लेकिन यह अधिक बोझिल है।


प्रकार सुरक्षा को संरक्षित करते समय सटीक प्रकार की सरणी प्राप्त करने के लिए जेनेरिक का उपयोग करने का तरीका यहां बताया गया है (जैसा कि अन्य उत्तरों के विपरीत है, जो आपको Object सरणी वापस देगा या संकलन समय पर चेतावनियों में परिणाम देगा):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

यह चेतावनियों के बिना संकलित करता है, और जैसा कि आप main रूप से देख सकते हैं, GenSet उदाहरण के रूप में आप जो भी प्रकार घोषित करते हैं, आप उस प्रकार की सरणी को असाइन कर सकते हैं, और आप उस तत्व के एक चर से a तत्व असाइन कर सकते हैं, जिसका अर्थ है कि सरणी में सरणी और मान सही प्रकार के हैं।

यह जावा ट्यूटोरियल में चर्चा के अनुसार, रनटाइम प्रकार टोकन के रूप में कक्षा अक्षर का उपयोग करके काम करता है। क्लास लिटरल को कंपाइलर द्वारा java.lang.Class उदाहरणों के रूप में माना जाता है। एक का उपयोग करने के लिए, बस क्लास के साथ कक्षा के नाम का पालन करें। तो, String.class क्लास String प्रतिनिधित्व करने वाली Class ऑब्जेक्ट के रूप में कार्य करता है। यह इंटरफेस, एनम्स, किसी भी आयामी सरणी (जैसे String[].class ), प्राइमेटिव्स (जैसे int.class ), और कीवर्ड void (यानी void.class ) के लिए भी काम करता है।

Class स्वयं सामान्य है ( Class<T> रूप में घोषित किया गया है, जहां T Class ऑब्जेक्ट का प्रतिनिधित्व करने वाले प्रकार के लिए खड़ा है), जिसका अर्थ है कि Class<String> का प्रकार Class<String>

इसलिए, जब भी आप GenSet लिए कन्स्ट्रक्टर को कॉल करते हैं, तो आप GenSet उदाहरण के घोषित प्रकार (उदाहरण के लिए String[].class GenSet<String> लिए String[].class ) के सरणी का प्रतिनिधित्व करने वाले पहले तर्क के लिए कक्षा शाब्दिक में प्रवेश करते हैं। ध्यान दें कि आप प्राइमेटिव्स की सरणी नहीं प्राप्त कर पाएंगे, क्योंकि प्राइमेटिव्स का उपयोग प्रकार चर के लिए नहीं किया जा सकता है।

कन्स्ट्रक्टर के अंदर, विधि cast कॉल करने से Class ऑब्जेक्ट द्वारा प्रतिनिधित्व किए गए Class में पास Object तर्क को कास्ट किया जाता है जिस पर विधि कहा जाता था। स्थिर विधि को कॉल करना java.lang.reflect.Array Object रूप में लौटाता है Class ऑब्जेक्ट द्वारा प्रतिनिधित्व प्रकार के सरणी को पहले तर्क के रूप में पारित किया गया है और दूसरी तर्क के रूप में पारित int द्वारा निर्दिष्ट लंबाई के रूप में पारित किया गया है। विधि को कॉल करने के लिए कॉम्पोनेंट टाइप Class ऑब्जेक्ट को Class ऑब्जेक्ट द्वारा प्रतिनिधित्व किए गए सरणी के घटक प्रकार का प्रतिनिधित्व करता है जिस पर विधि कहा जाता था (उदाहरण के लिए String[].class लिए String[].class , Class ऑब्जेक्ट किसी सरणी का प्रतिनिधित्व नहीं करता है) ।

वह अंतिम वाक्य पूरी तरह सटीक नहीं है। कॉलिंग String[].class.getComponentType() क्लास String प्रतिनिधित्व करने वाली Class ऑब्जेक्ट देता है, लेकिन इसका प्रकार Class<?> , Class<String> , यही कारण है कि आप निम्न की तरह कुछ नहीं कर सकते हैं।

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Class में हर विधि के लिए जाता है जो Class ऑब्जेक्ट देता है।

इस जवाब पर जोआचिम सॉर की टिप्पणी के बारे में (मेरे पास इस पर टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा नहीं है), T[] को कास्ट का उपयोग करने का उदाहरण चेतावनी देगा क्योंकि संकलक उस मामले में प्रकार की सुरक्षा की गारंटी नहीं दे सकता है।

इनगो की टिप्पणियों के बारे में संपादित करें:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}

मुझे आश्चर्य है कि क्या यह कोड प्रभावी जेनेरिक सरणी बनाएगा?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

संपादित करें: शायद इस तरह की एक सरणी बनाने का एक वैकल्पिक तरीका, यदि आवश्यक आकार आपको ज्ञात और छोटा था, तो केवल "null" s की आवश्यक संख्या को शून्यArray कमांड में फ़ीड करना होगा?

हालांकि स्पष्ट रूप से यह createArray कोड का उपयोग करने के रूप में बहुमुखी नहीं है।


मुझे एक त्वरित और आसान तरीका मिला है जो मेरे लिए काम करता है। ध्यान दें कि मैंने जावा जावा जेडीके 8 पर इसका इस्तेमाल किया है। मुझे नहीं पता कि यह पिछले संस्करणों के साथ काम करेगा या नहीं।

हालांकि हम एक विशिष्ट प्रकार पैरामीटर की जेनेरिक सरणी को तुरंत चालू नहीं कर सकते हैं, हम एक जेनेरिक क्लास कन्स्ट्रक्टर में पहले से बनाए गए सरणी को पास कर सकते हैं।

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

अब मुख्य में हम सरणी बना सकते हैं:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

अपने सरणी के साथ अधिक लचीलापन के लिए आप एक लिंक्ड सूची का उपयोग कर सकते हैं उदाहरण के लिए। ArrayList और Java.util.ArrayList क्लास में पाए गए अन्य तरीकों।


मुझे बदले में एक प्रश्न पूछना है: क्या आपका GenSet "चेक" या "अनचेक" है? इसका क्या मतलब है?

  • चेक किया गया : मजबूत टाइपिंगGenSet स्पष्ट रूप से जानता है कि इसमें किस प्रकार की ऑब्जेक्ट्स शामिल हैं (यानी इसके कन्स्ट्रक्टर को Class<E> तर्क के साथ स्पष्ट रूप से बुलाया गया था, और विधियों को एक अपवाद फेंक दिया जाएगा जब वे पास किए गए तर्क हैं जो टाइप E नहीं हैं। Collections.checkedCollection देखें। चेक किए गए Collections.checkedCollection

    -> उस मामले में, आपको लिखना चाहिए:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
    
  • अनचेक : कमजोर टाइपिंग । तर्क के रूप में पारित किसी भी वस्तु पर वास्तव में कोई प्रकार की जांच नहीं की जाती है।

    -> उस मामले में, आपको लिखना चाहिए

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }
    

    ध्यान दें कि सरणी का घटक प्रकार टाइप पैरामीटर का erasure होना चाहिए:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }
    

जावा में जेनेरिक की जानबूझकर, जानबूझकर, कमजोरी से ये सभी परिणाम: इसे मिटाने के द्वारा लागू किया गया था, इसलिए "जेनेरिक" वर्ग नहीं जानते कि वे रन टाइम पर किस प्रकार के तर्क के साथ बनाए गए थे, और इसलिए टाइप- सुरक्षा जब तक कि कुछ स्पष्ट तंत्र (प्रकार-जांच) लागू नहीं किया जाता है।


मूल्यों की एक सूची पास ...

public <T> T[] array(T... values) {
    return values;
}

मैंने इस कोड स्निपेट को एक सरल स्वचालित परीक्षण उपयोगिता के लिए पारित कक्षा को तुरंत प्रतिबिंबित करने के लिए बनाया है।

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

इस खंड को नोट करें:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

सरणी शुरू करने के लिए जहां Array.newInstance (सरणी का वर्ग, सरणी का आकार) । कक्षा दोनों आदिम (int.class) और वस्तु (Integer.class) हो सकती है।

BeanUtils वसंत का हिस्सा है।


यह एकमात्र उत्तर है जो सुरक्षित प्रकार है

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

हाय हालांकि धागा मर चुका है, मैं इस पर आपका ध्यान आकर्षित करना चाहता हूं:

जेनिक्स का उपयोग संकलन समय के दौरान प्रकार की जांच के लिए किया जाता है:

  • इसलिए इसका उद्देश्य यह जांचना है कि जो कुछ आता है वह आपको चाहिए।
  • आप जो वापस लौटते हैं वह उपभोक्ता की ज़रूरत है।
  • इसे देखो:

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


Generic array creation is disallowed in java but you can do it like

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}

private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}




instantiation