¿Cómo evitar la duplicación de código con respecto a los tipos primitivos?

9

Antecedentes

Una secuencia de entrada de bits está respaldada por una matriz de bytes. Hay un puñado de métodos que leen de esa matriz de bytes en varias matrices primitivas forzadas.

Problema

Hay un código duplicado. Java carece de genéricos en los tipos primitivos, por lo que tal vez la repetición sea inevitable.

Código

El código repetitivo es evidente en los siguientes métodos:

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

Tenga en cuenta cómo se final byte[] outrelaciona con readByte(bits)lo que se final short[] outrelaciona con readShort(bits). Estas relaciones son el quid de la cuestión.

Pregunta

¿Cómo se puede eliminar la duplicación, en todo caso, sin incurrir en un impacto significativo en el rendimiento (por ejemplo, mediante autoboxing)?

Relacionado

Dave Jarvis
fuente
66
No, nada que puedas hacer allí. La duplicación es la única opción.
Andy Turner
Use una colección primitiva de terceros
Vince Emigh
1
Java lacks generics on primitive types, so perhaps the repetition is unavoidable.Sip. (Por lo general, no es un gran problema, ya que es raro que un programa necesite más que unas pocas primitivas diferentes. También puede "arreglar" esto poniendo primitivas dentro de una clase y usando la serialización de objetos, aunque eso puede ser relativamente lento. )
markspace
3
Además, (acabo de recordar esto) si está leyendo primitivas masivas como parece indicar su código, utilizando ByteBuffermétodos comoasDoubleBuffer() o asShortBuffer()descargará algunos de los trabajos de nivel más bajo. docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/…
markspace
1
Tenga en cuenta que hay algunos esfuerzos para brindar soporte genérico primitivo a Java, es decir, List<int>etc. Lanzamiento en unos 2-5 años más o menos. Se llama Proyecto Valhalla.
Zabuzard

Respuestas:

2

Si está leyendo primitivas masivas como parece indicar su código, el uso de métodos ByteBuffer como asDoubleBuffer () o asShortBuffer () descargará algunos de los trabajos de nivel más bajo.

Ejemplo:

   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`
   }

(¡El código se compila pero no se prueba!)

espacio de marcas
fuente
0

Una posibilidad, que incurrirá en una penalización de rendimiento, es usar java.lang.reflect.Array para tratar la matriz como un Objeto que luego permite reutilizar el mismo código en todos los métodos de lectura.

@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 duplicación se ha abordado a costa de algún rendimiento, una falta menor de seguridad de tipo de tiempo de compilación y el uso de la reflexión.

Dave Jarvis
fuente