Saisir un segment D'un tableau en Java sans créer un nouveau tableau sur heap

je cherche une méthode en Java qui retournera un segment d'un tableau. Un exemple serait d'obtenir le tableau d'octets contenant les 4 et 5 octets d'un tableau d'octets. Je ne veux pas avoir à créer un nouveau tableau d'octets dans la mémoire de masse juste pour le faire. En ce moment j'ai le code suivant:

doSomethingWithTwoBytes(byte[] twoByteArray);

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

j'aimerais savoir s'il y avait un moyen de faire simplement doSomething(bigArray.getSubArray(4, 2)) où 4 est l'offset et 2 est la longueur, par exemple.

176

16 réponses

Avertissement: Cette réponse n'est pas conforme aux contraintes de la question:

je ne veux pas avoir à créer un nouveau tableau d'octets dans la mémoire de masse juste pour le faire.

( Honnêtement, je sens que ma réponse est digne de suppression. La réponse de @unique72 est correcte. Imma laisser cette édition se reposer un peu et puis je vais supprimer cette réponse. )


Je ne sais pas comment faire cela directement avec des tableaux sans allocation de tas supplémentaire, mais les autres réponses utilisant une sous-liste wrapper ont une allocation supplémentaire pour le wrapper seulement – mais pas le tableau – qui serait utile dans le cas d'un grand tableau.

qui dit, si on cherche la brièveté, la méthode utilitaire Arrays.copyOfRange() a été introduite dans Java 6 (fin 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);
179
répondu David J. Liszewski 2014-10-10 22:54:50

Arrays.asList(myArray) délègue au nouveau ArrayList(myArray) , qui ne copie pas le tableau mais stocke simplement la référence. En utilisant List.subList(start, end) après cela fait un SubList qui ne fait que renvoyer à la liste originale (qui ne fait encore que renvoyer au tableau). Aucune copie du tableau ou de son contenu, juste la création de wrapper, et toutes les listes impliquées sont soutenues par le tableau original. (Je pensais que ça serait plus lourd.)

162
répondu unique72 2014-12-16 15:24:55

si vous cherchez une approche d'alias de style pointeur, de sorte que vous n'avez même pas besoin d'allouer de l'espace et de copier les données, alors je crois que vous n'avez pas de chance.

System.arraycopy() copiera de votre source à destination, et l'efficacité est revendiquée pour cet utilitaire. Vous devez allouer le tableau de destination.

40
répondu djna 2016-01-17 18:06:45

une façon est d'envelopper le tableau dans java.nio.ByteBuffer , utiliser les fonctions Absolute put/get, et couper le tampon pour travailler sur un subarray.

par exemple:

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());
}

notez que vous devez appeler à la fois wrap() et slice() , puisque wrap() n'affecte par lui-même que les fonctions put/get relatives, pas les absolues.

ByteBuffer peut être un peu difficile à comprendre, mais est très probablement efficace mis en œuvre et qui vaut la peine d'être appris.

21
répondu Soulman 2018-01-31 17:38:29

utilisez java.nio.Buffer's. C'est une enveloppe légère pour les tampons de différents types primitifs et aide à gérer le tranchage, la position, la conversion, la commande de bytes, etc.

si vos octets proviennent d'un flux, les tampons NIO peuvent utiliser le" mode direct " qui crée un tampon soutenu par des ressources natives. Cela peut améliorer les performances dans beaucoup de cas.

20
répondu James Schek 2009-07-08 20:52:03

vous pouvez utiliser le ArrayUtils.subarray apache commons. Pas parfait mais un peu plus intuitif que System.arraycopy. L'inconvénient est qu'il introduit une autre dépendance dans votre code.

15
répondu seth 2009-07-08 20:42:13

je vois que la réponse de la sous-liste est déjà ici, Mais voici le code qui démontre que c'est une vraie sous-Liste, pas une copie:

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));
    }
}

Je ne crois pas qu'il y ait une bonne façon de le faire directement avec les tableaux, cependant.

10
répondu Carl Manaster 2009-07-08 20:40:28
List.subList(int startIndex, int endIndex)
9
répondu Manuel Selva 2015-02-07 20:20:26

une option serait de passer l'ensemble du tableau et les indices de début et de fin, et d'itérer entre ceux-ci au lieu d'itérer sur l'ensemble du tableau passé.

void method1(byte[] array) {
    method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
    for ( int i = start; i <= end; i++ ) {
        ....
    }
}
7
répondu Sam DeFabbia-Kane 2009-07-08 20:44:40

les List s vous permettent d'utiliser et de travailler avec subList de quelque chose de transparent. Les tableaux primitifs vous demanderaient de garder une trace d'une sorte de limite d'offset. ByteBuffer s ont des options similaires que j'ai entendu.

Edit: Si vous êtes en charge de la méthode utile, vous pouvez simplement la définir avec des limites (comme fait dans de nombreuses méthodes liées aux tableaux dans java lui-même:

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

Il n'est pas clair, cependant, si vous travaillez sur les éléments du tableau eux-mêmes, par exemple vous calculez quelque chose et vous écrivez le résultat?

6
répondu akarnokd 2009-07-08 20:45:43

les références Java pointent toujours vers un objet. L'objet a un en-tête qui, entre autres choses, identifie le type de béton (de sorte que les moulages peuvent échouer avec ClassCastException ). Pour les tableaux, le début de l'objet inclut aussi la longueur, les données suivent immédiatement après en mémoire (techniquement une implémentation est libre de faire ce qu'elle veut, mais ce serait stupide de faire autre chose). Donc, vous ne pouvez pas avoir une référence qui pointe quelque part dans un tableau.

In C les pointeurs n'importe où et à n'importe quoi, et vous pouvez pointer vers le milieu d'un tableau. Mais vous ne pouvez pas lancer en toute sécurité ou de savoir combien de temps le tableau est. En D le pointeur contient un offset dans le bloc mémoire et la longueur (ou de façon équivalente un pointeur à la fin, je ne peux pas me rappeler ce que l'implémentation fait réellement). Cela permet à D de découper des matrices. Dans C++, il y a deux itérateurs pointant vers le début et la fin, mais C++ est un peu bizarre comme ça.

donc revenir à Java, non vous ne pouvez pas. Comme mentionné, NIO ByteBuffer vous permet d'envelopper un tableau et puis le trancher, mais donne une interface maladroite. Vous pouvez bien sûr copier, ce qui est probablement beaucoup plus rapide que vous le pensez. Vous pouvez introduire votre propre abstraction String -comme cela vous permet de trancher un tableau (l'actuelle implémentation Sun de String a une référence char[] plus un offset de départ et la longueur, l'implémentation haute performance a juste le char[] ). byte[] est faible niveau, mais toute abstraction basée sur la classe que vous mettez sur cela va faire un gâchis terrible de la syntaxe, jusqu'à JDK7 (peut-être).

4
répondu Tom Hawtin - tackline 2009-07-08 21:32:25

@unique72 réponse comme une fonction ou une ligne simple, vous pourriez avoir besoin de remplacer L'objet, par le type de classe respectif que vous souhaitez 'trancher'. Deux variantes sont proposées pour répondre à différents besoins.

/// 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();
}
2
répondu PicoCreator 2015-02-07 10:55:38

Que Diriez-vous d'un papier mince 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;
      }
   };
}

(non testé)

1
répondu RoToRa 2011-01-14 14:34:49

j'avais besoin de parcourir la fin d'un tableau et je ne voulais pas copier le tableau. Mon approche était de faire un itérable sur le tableau.

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
répondu Owen O'Malley 2016-06-27 02:58:36

C'est un peu plus léger que les Tableaux.copyOfRange - pas de plage ou négatif

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;
}
0
répondu bond 2014-06-06 05:29:34

s'il vous Plaît utiliser

System.arrayCopy();

Il permet de spécifier la position de départ du tableau source et le nombre d'éléments que vous souhaitez copier.

0
répondu kk1957 2016-10-07 17:07:10