Comment éviter la duplication de code concernant les types primitifs?

9

Contexte

Un flux d'entrée de bits est soutenu par un tableau d'octets. Il existe une poignée de méthodes qui lisent ce tableau d'octets dans divers tableaux primitifs sous contrainte.

Problème

Il y a du code en double. Java manque de génériques sur les types primitifs, donc la répétition est peut-être inévitable.

Code

Le code répétitif est apparent dans les méthodes suivantes:

@Override
public long readBytes(final byte[] out, final int offset, final int count, final int bits) {
    final int total = offset + count;

    assert out != null;
    assert total <= out.length;

    final long startPosition = position();

    for (int i = offset; i < total; i++) {
        out[i] = readByte(bits);
    }

    return position() - startPosition;
}

@Override
public long readShorts(final short[] out, final int offset, final int count, final int bits) {
    final int total = offset + count;

    assert out != null;
    assert total <= out.length;

    final long startPosition = position();

    for (int i = offset; i < total; i++) {
        out[i] = readShort(bits);
    }

    return position() - startPosition;
}

Notez comment se final byte[] outrapporte à readByte(bits)tout comme se final short[] outrapporte à readShort(bits). Ces relations sont au cœur du problème.

Question

Comment la duplication peut-elle être éliminée, le cas échéant, sans encourir un impact significatif sur les performances (par exemple, par autoboxing)?

en relation

Dave Jarvis
la source
6
Non, vous ne pouvez rien y faire. La duplication est la seule option.
Andy Turner
Utilisez une collection primitive tierce
Vince Emigh
1
Java lacks generics on primitive types, so perhaps the repetition is unavoidable.Ouaip. (Habituellement, ce n'est pas vraiment un problème, car il est rare qu'un programme ait besoin de plusieurs primitives différentes. Vous pouvez également "corriger" cela en plaçant des primitives dans une classe et en utilisant la sérialisation d'objets, bien que cela puisse être relativement lent. )
markspace
3
En outre, (souvenez-vous juste de cela) si vous lisez des primitives en vrac comme votre code semble l'indiquer, en utilisant des ByteBufferméthodes comme asDoubleBuffer()ou asShortBuffer()déchargera certains des travaux de niveau le plus bas. docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/…
markspace
1
Notez qu'il y a des efforts pour apporter un support générique primitif à Java, c'est-à-dire, List<int>etc. Sortie dans environ 2 à 5 ans. Il s'agit du projet Valhalla.
Zabuzard

Réponses:

2

Si vous lisez des primitives en vrac comme votre code semble indiquer, en utilisant ByteBuffer méthodes comme asDoubleBuffer () ou asShortBuffer () déchargeront certains des travaux plus bas niveau.

Exemple:

   public void readBytes( final byte[] out, final int offset, final int count, final ByteBuffer buffer ) {
      buffer.get( out, offset, count );  // udates ByteBuffer `position` automatically
   }

   public void readShorts( final short[] out, final int offset, final int count, final ByteBuffer buffer ) {
      ShortBuffer sb = buffer.asShortBuffer();
      sb.get( out, offset, count );  // note that `count` reads two bytes for each `short`
   }

(Le code se compile mais n'est pas testé!)

espace de marquage
la source
0

Une possibilité, qui entraînera une baisse des performances, consiste à utiliser java.lang.reflect.Arraypour traiter le tableau comme un objet qui permet ensuite de réutiliser le même code dans toutes les méthodes de lecture.

@FunctionalInterface
public interface BitArrayReader {
    Object read(int bits);
}

private long readPrimitive(
        final Object out, final int offset, final int count, final int bits,
        final BitArrayReader reader) {
    final int total = offset + count;

    assert out != null;
    assert total <= Array.getLength(out);

    final long startPosition = position();

    for (int i = offset; i < total; i++) {
        Array.set(out, i, reader.read(bits));
    }

    return position() - startPosition;
}

@Override
public long readBooleans(boolean[] out, int offset, int count, int bits) {
    return readPrimitive(out, offset, count, bits, this::readBoolean);
}

La duplication a été corrigée au prix de certaines performances, d'un manque mineur de sécurité de type à la compilation et de l'utilisation de la réflexion.

Dave Jarvis
la source