ढेर पर एक नई सरणी बनाने के बिना जावा में एक सरणी के एक खंड को पकड़ो


181

मैं जावा में एक विधि की तलाश कर रहा हूं जो एक सरणी के एक खंड को लौटाएगा। एक उदाहरण बाइट सरणी के 4 वें और 5 वें बाइट वाले बाइट सरणी को प्राप्त करना होगा। मैं ढेर स्मृति में सिर्फ करने के लिए एक नया बाइट सरणी बनाना नहीं चाहता। अभी मेरे पास निम्नलिखित कोड है:

doSomethingWithTwoBytes(byte[] twoByteArray);

void someMethod(byte[] bigArray)
{
      byte[] x = {bigArray[4], bigArray[5]};
      doSomethingWithTwoBytes(x);
}

मैं जानना चाहूंगा कि क्या कोई ऐसा तरीका है doSomething(bigArray.getSubArray(4, 2))जहां 4 की भरपाई हो और 2 की लंबाई हो, उदाहरण के लिए।


1
सी ++ में कुछ जेएनआई जादू करने के बारे में क्या? जीसी पीओवी से एक आपदा हो सकती है?
एलिकएल्ज़िन-किलाका

क्या इसे आदिम बाइट्स की एक सरणी होना चाहिए?
एमपी कोरस्टंज

जवाबों:


185

अस्वीकरण: यह उत्तर प्रश्न की बाधाओं के अनुरूप नहीं है:

मैं ढेर स्मृति में सिर्फ करने के लिए एक नया बाइट सरणी बनाना नहीं चाहता।

( ईमानदारी से, मुझे लगता है कि मेरा उत्तर विलोपन के योग्य है। @ unique72 द्वारा उत्तर सही है। इम्मा ने इस संपादन को थोड़ी देर के लिए बैठने दिया और फिर मैं इस उत्तर को हटा दूंगा। )


मुझे अतिरिक्त ढेर आवंटन के बिना सरणियों के साथ सीधे ऐसा करने का तरीका नहीं पता है, लेकिन उप-सूची आवरण का उपयोग करने वाले अन्य उत्तरों में केवल आवरण के लिए अतिरिक्त आवंटन है - लेकिन ऐसा नहीं है - जो मामले में उपयोगी होगा एक बड़ी सरणी।

उस ने कहा, यदि कोई संक्षिप्तता की तलाश में है, तो उपयोगिता विधि Arrays.copyOfRange()जावा 6 (2006 के अंत में?) में पेश की गई थी।

byte [] a = new byte [] {0, 1, 2, 3, 4, 5, 6, 7};

// get a[4], a[5]

byte [] subArray = Arrays.copyOfRange(a, 4, 6);

10
यह अभी भी गतिशील रूप से एक नया मेमोरी सेगमेंट आवंटित करता है और उस सीमा को कॉपी करता है।
डैन

4
धन्यवाद Dan - मैंने इस बात की उपेक्षा की कि ओपी नया ऐरे बनाना नहीं चाहता था और मैं इसके क्रियान्वयन को नहीं देखता था copyOfRange। अगर यह बंद-स्रोत होता तो शायद यह पास हो सकता था। :)
डेविड जे। लिस्ज़ेवस्की

7
मुझे लगता है कि बहुत से लोग एक सरणी से एक उप-सरणी बनाना चाहते हैं और चिंतित नहीं हैं कि यह कुछ और मेमोरी का उपयोग करता है। वे इस सवाल पर आते हैं और वे चाहते हैं कि उत्तर प्राप्त करें - इसलिए कृपया इसे हटाएं नहीं क्योंकि यह उपयोगी है - मुझे लगता है कि यह ठीक है।
लोनली कोडर

2
वास्तव में, copyOfRange अभी भी नए मेमोरी सेगमेंट को आवंटित करता है
केविंगो त्सै

167

Arrays.asList(myArray)नए प्रतिनिधि ArrayList(myArray), जो सरणी की प्रतिलिपि नहीं बनाता है, लेकिन केवल संदर्भ संग्रहीत करता है। इसके List.subList(start, end)बाद का उपयोग करना, SubListजो मूल सूची का संदर्भ देता है (जो अभी भी केवल सरणी को संदर्भित करता है)। सरणी या उसकी सामग्री की कोई प्रतिलिपि नहीं, बस आवरण निर्माण, और शामिल सभी सूचियाँ मूल सरणी द्वारा समर्थित हैं। (मुझे लगा कि यह भारी होगा।)


9
स्पष्ट करने के लिए, यह एक निजी वर्ग को Arraysभ्रमित करने के लिए बुलाता है ArrayList, लेकिन जो वास्तव में एक Listसरणी के चारों ओर है, java.util.ArrayListजिसके विपरीत एक प्रतिलिपि बनाई जाएगी। कोई नया आवंटन (सूची की सामग्री), और कोई तृतीय पक्ष निर्भरता नहीं। यह मेरा मानना ​​है, सबसे सही उत्तर है।
dimo414

28
असल में, यह आदिम प्रकार सरणियों के लिए काम नहीं करेगा जैसा कि ओपी चाहता था ( byte[]उसके मामले में)। सब तुम्हें मिलेगा List<byte[]>। और एक महत्वपूर्ण मेमोरी ओवरहेड लगाने के byte[] bigArrayलिए बदल Byte[] bigArrayसकता है।
दिमित्री एवोन्टोमोव

2
sun.misc.Unsafeकक्षा के माध्यम से वास्तव में जो वांछित है उसे प्राप्त करने का एकमात्र तरीका है ।
दिमित्री एवोन्टोमोव

39

यदि आप एक पॉइंटर स्टाइल अलियासिंग दृष्टिकोण की मांग कर रहे हैं, ताकि आपको अंतरिक्ष आवंटित करने और डेटा को कॉपी करने की आवश्यकता न हो, तो मुझे विश्वास है कि आप भाग्य से बाहर हैं।

System.arraycopy() अपने स्रोत से गंतव्य तक कॉपी करेगा, और इस उपयोगिता के लिए दक्षता का दावा किया जाता है। आपको गंतव्य सरणी आवंटित करने की आवश्यकता है।


3
हां, मैं किसी तरह की पॉइंटर विधि की उम्मीद कर रहा था क्योंकि मैं गतिशील रूप से स्मृति आवंटित नहीं करना चाहता। लेकिन ऐसा लगता है कि मुझे क्या करना है।
जुबला

1
जैसा कि @ यूनिक 72 बताता है, विभिन्न जावा सूची या सरणी प्रकारों के कार्यान्वयन में सूक्ष्मता का दोहन करके आप जो चाहते हैं, करने के तरीके प्रतीत होते हैं। यह संभव प्रतीत होता है, बस स्पष्ट तरीके से नहीं और इससे मुझे इस पर भरोसा करने में झिझक होती है ...
एंड्रयू

array*copy*()उसी मेमोरी का पुन: उपयोग क्यों करना चाहिए ? क्या यह बिल्कुल विपरीत नहीं है कि एक कॉलर क्या उम्मीद करेगा?
पैट्रिक

23

एक तरीका यह है कि एरे को लपेटें java.nio.ByteBuffer, निरपेक्ष पुट / फंक्शन्स का उपयोग करें, और सबर्रे पर काम करने के लिए बफर को स्लाइस करें।

उदाहरण के लिए:

doSomething(ByteBuffer twoBytes) {
    byte b1 = twoBytes.get(0);
    byte b2 = twoBytes.get(1);
    ...
}

void someMethod(byte[] bigArray) {
      int offset = 4;
      int length = 2;
      doSomething(ByteBuffer.wrap(bigArray, offset, length).slice());
}

ध्यान दें कि आपको दोनों को कॉल करना होगा wrap()और slice()चूंकि wrap()स्वयं ही केवल सापेक्ष पुट / फंक्शन को प्रभावित करता है, न कि पूर्ण को।

ByteBuffer समझने के लिए थोड़ा मुश्किल हो सकता है, लेकिन सबसे अधिक संभावना कुशलतापूर्वक लागू की जाती है, और अच्छी तरह से सीखने लायक है।


1
यह भी ध्यान देने योग्य है कि बाइटबफ़र ऑब्जेक्ट्स को आसानी से डिकोड किया जा सकता है:StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buffer, 0, readBytes))
skeryl

@Soulman स्पष्टीकरण के लिए धन्यवाद, लेकिन एक सवाल यह उपयोग करने की तुलना में अधिक कुशल है Arrays.copyOfRange?
ucMedia

1
दो-बाइट सरणी के लिए @ucMedia, Arrays.copyOfRangeशायद अधिक कुशल है। आम तौर पर, आपको अपने विशिष्ट उपयोग के मामले में मापना होगा।
सोलमैन

20

Java.nio.Buffer का उपयोग करें। यह विभिन्न आदिम प्रकार के बफ़र्स के लिए एक हल्का आवरण है और स्लाइसिंग, स्थिति, रूपांतरण, बाइट ऑर्डरिंग आदि का प्रबंधन करने में मदद करता है।

यदि आपकी बाइट्स स्ट्रीम से उत्पन्न होती हैं, तो NIO बफ़र्स "डायरेक्ट मोड" का उपयोग कर सकते हैं जो मूल संसाधनों द्वारा समर्थित बफर बनाता है। यह कई मामलों में प्रदर्शन में सुधार कर सकता है।


14

आप अपाचे कॉमन्स में ArrayUtils.subarray का उपयोग कर सकते हैं । सही नहीं है लेकिन System.arraycopy. नकारात्मक पक्ष की तुलना में थोड़ा अधिक सहज है कि यह आपके कोड में एक और निर्भरता का परिचय देता है।


23
यह Java 1.6 में Arrays.copyOfRange () के समान है
newacct

10

मैं देखता हूं कि उप-उत्तर पहले से ही यहां है, लेकिन यहां एक कोड है जो दर्शाता है कि यह एक सच्ची सूची है, प्रतिलिपि नहीं:

public class SubListTest extends TestCase {
    public void testSubarray() throws Exception {
        Integer[] array = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(array);
        List<Integer> subList = list.subList(2, 4);
        assertEquals(2, subList.size());
        assertEquals((Integer) 3, subList.get(0));
        list.set(2, 7);
        assertEquals((Integer) 7, subList.get(0));
    }
}

मुझे विश्वास नहीं है कि एरे के साथ सीधे ऐसा करने का एक अच्छा तरीका है, हालांकि।



6

एक विकल्प पूरे सरणी और प्रारंभ और अंत सूचक को पास करना होगा, और उन लोगों के बीच पुनरावृति होगा जो पूरे सरणी से अधिक पुनरावृत्त होने के बजाय।

void method1(byte[] array) {
    method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
    for ( int i = start; i <= end; i++ ) {
        ....
    }
}

6

Listरों आप के साथ उपयोग के लिए और काम करने की अनुमति subListपारदर्शी रूप से कुछ की। आदिम सरणियों को आपको कुछ प्रकार की ऑफसेट - सीमा का ट्रैक रखने की आवश्यकता होगी। ByteBufferमैंने जैसा सुना है उसके समान विकल्प हैं।

संपादित करें: यदि आप उपयोगी विधि के प्रभारी हैं, तो आप इसे सीमा के साथ परिभाषित कर सकते हैं (जैसा कि जावा में कई सरणी से संबंधित विधियों में किया गया है:

doUseful(byte[] arr, int start, int len) {
    // implementation here
}
doUseful(byte[] arr) {
    doUseful(arr, 0, arr.length);
}

यह स्पष्ट नहीं है, हालांकि, यदि आप सरणी तत्वों पर स्वयं काम करते हैं, उदाहरण के लिए, आप कुछ गणना करते हैं और परिणाम वापस लिखते हैं?


6

जावा संदर्भ हमेशा किसी वस्तु की ओर इशारा करता है। ऑब्जेक्ट में एक हेडर होता है जो अन्य चीजों के बीच कंक्रीट प्रकार की पहचान करता है (ताकि जातियां विफल हो सकती हैं ClassCastException)। सरणियों के लिए, ऑब्जेक्ट की शुरुआत में लंबाई भी शामिल है, डेटा फिर मेमोरी में तुरंत बाद में आता है (तकनीकी रूप से एक कार्यान्वयन वह क्या करना चाहता है करने के लिए स्वतंत्र है, लेकिन यह और कुछ भी करने के लिए बेतरतीब होगा)। तो, आप ऐसा नहीं कर सकते हैं जो एक संदर्भ है जो एक सरणी में कहीं इंगित करता है।

C संकेत में कहीं भी और कुछ भी इंगित करते हैं, और आप किसी सरणी के बीच में इंगित कर सकते हैं। लेकिन आप सुरक्षित रूप से यह पता नहीं लगा सकते हैं कि सरणी कितनी लंबी है। डी में पॉइंटर में मेमोरी ब्लॉक और लंबाई में एक ऑफसेट होता है (या समतुल्य रूप से एक पॉइंटर अंत तक, मुझे याद नहीं है कि कार्यान्वयन वास्तव में क्या करता है)। यह डी को स्लाइस एरे की अनुमति देता है। C ++ में आपके पास प्रारंभ और अंत की ओर इशारा करते हुए दो पुनरावृत्तियाँ होंगी, लेकिन C ++ कुछ अजीब है।

तो वापस जावा में जा रहे हैं, नहीं, तुम नहीं कर सकते। जैसा कि उल्लेख किया गया है, एनआईओ ByteBufferआपको एक सरणी लपेटने और फिर इसे स्लाइस करने की अनुमति देता है, लेकिन एक अजीब इंटरफ़ेस देता है। आप निश्चित रूप से कॉपी कर सकते हैं, जो आपके विचार से शायद बहुत तेज है। आप अपने खुद के समान Stringअमूर्तता का परिचय दे सकते हैं जो आपको एक सरणी को स्लाइस करने की अनुमति देता है (वर्तमान सूर्य कार्यान्वयन Stringमें एक char[]संदर्भ प्लस एक स्टार्ट ऑफसेट और लंबाई, उच्च प्रदर्शन कार्यान्वयन बस है char[])। byte[]निम्न स्तर है, लेकिन आपके द्वारा डाला गया कोई भी वर्ग-आधारित अमूर्त वाक्यविन्यास का एक भयानक गड़बड़ करने वाला है, जब तक कि JDK7 (शायद)।


यह समझाने के लिए धन्यवाद कि यह असंभव क्यों होगा। Btw, स्ट्रिंग अब substringहॉटस्पॉट में कॉपी करता है (भूल जाओ कि यह किसने बनाया है)। आप क्यों कहते हैं कि JDK7 बाइटबफर की तुलना में बेहतर सिंटैक्स की अनुमति देगा?
१४:०४

@AleksandrDubinsky लिखने के समय ऐसा लग रहा था कि जावा SE 7 []उपयोगकर्ता-परिभाषित प्रकारों पर सरणी संकेतन की अनुमति देने जा रहा है , जैसे Listऔर ByteBuffer। अभी भी इंतज़ार कर रहे हैं ...
टॉम हॉल्टिन -

2

@ एक साधारण फ़ंक्शन या लाइन के रूप में अनूठे जवाब, आपको ऑब्जेक्ट को बदलने की आवश्यकता हो सकती है, संबंधित वर्ग प्रकार के साथ जिसे आप 'स्लाइस' करना चाहते हैं। विभिन्न आवश्यकताओं के अनुरूप दो संस्करण दिए गए हैं।

/// Extract out array from starting position onwards
public static Object[] sliceArray( Object[] inArr, int startPos ) {
    return Arrays.asList(inArr).subList(startPos, inArr.length).toArray();
}

/// Extract out array from starting position to ending position
public static Object[] sliceArray( Object[] inArr, int startPos, int endPos ) {
    return Arrays.asList(inArr).subList(startPos, endPos).toArray();
}

1

कैसे एक पतली Listआवरण के बारे में ?

List<Byte> getSubArrayList(byte[] array, int offset, int size) {
   return new AbstractList<Byte>() {
      Byte get(int index) {
         if (index < 0 || index >= size) 
           throw new IndexOutOfBoundsException();
         return array[offset+index];
      }
      int size() {
         return size;
      }
   };
}

(Untested)


यह बाइट्स के बॉक्सिंग-अनबॉक्सिंग को उकसाएगा। धीमा हो सकता है।
एमपी कोरस्टंज

@mpkorstanje: ओरेबल जावा लाइब्रेरी में Byteसभी byteमूल्यों के लिए ऑब्जेक्ट कैश्ड हैं। इसलिए बॉक्सिंग ओवरहेड धीमी होनी चाहिए।
Lii

1

मुझे एक सरणी के अंत के माध्यम से पुनरावृति की आवश्यकता थी और वह सरणी की प्रतिलिपि नहीं बनाना चाहता था। मेरा दृष्टिकोण सरणी पर एक Iterable बनाना था।

public static Iterable<String> sliceArray(final String[] array, 
                                          final int start) {
  return new Iterable<String>() {
    String[] values = array;
    int posn = start;

    @Override
    public Iterator<String> iterator() {
      return new Iterator<String>() {
        @Override
        public boolean hasNext() {
          return posn < values.length;
        }

        @Override
        public String next() {
          return values[posn++];
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException("No remove");
        }
      };
    }
  };
}

-1

यह Arrays.copyOfRange की तुलना में थोड़ा अधिक हल्का है - कोई सीमा या नकारात्मक नहीं

public static final byte[] copy(byte[] data, int pos, int length )
{
    byte[] transplant = new byte[length];

    System.arraycopy(data, pos, transplant, 0, length);

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