Wie vermeide ich doppelten Code in Bezug auf primitive Typen?

9

Hintergrund

Ein Bit-Eingabestream wird durch ein Array von Bytes unterstützt. Es gibt eine Handvoll Methoden, die von diesem Byte-Array in verschiedene erzwungene primitive Arrays lesen.

Problem

Es gibt doppelten Code. In Java fehlen Generika für primitive Typen, daher ist die Wiederholung möglicherweise unvermeidbar.

Code

Der sich wiederholende Code wird bei den folgenden Methoden deutlich:

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

Beachten Sie, wie final byte[] outsich auf readByte(bits)genau wie final short[] outbezieht readShort(bits). Diese Beziehungen sind der Kern des Problems.

Frage

Wie kann die Duplizierung, wenn überhaupt, beseitigt werden, ohne dass ein erheblicher Leistungseinbruch auftritt (z. B. durch Autoboxing)?

verbunden

Dave Jarvis
quelle
6
Nein, da kannst du nichts machen. Duplizierung ist die einzige Option.
Andy Turner
Verwenden Sie eine primitive Sammlung eines Drittanbieters
Vince Emigh
1
Java lacks generics on primitive types, so perhaps the repetition is unavoidable.Jep. (Normalerweise ist dies kein großes Problem, da ein Programm selten mehr als ein paar verschiedene Grundelemente benötigt. Sie können dies auch "beheben", indem Sie Grundelemente in eine Klasse einfügen und die Objekt-Serialisierung verwenden, obwohl dies relativ langsam sein kann. )
Markspace
3
Auch (gerade daran erinnert), wenn Sie Massenprimitive lesen, wie es Ihr Code zu zeigen scheint, verwenden Sie ByteBufferMethoden wie asDoubleBuffer()oder asShortBuffer()werden einige der Arbeiten der untersten Ebene auslagern. docs.oracle.com/de/java/javase/11/docs/api/java.base/java/nio/…
markspace
1
Beachten Sie, dass einige Anstrengungen unternommen werden, um Java primitive generische Unterstützung zu bieten, z. B. List<int>Veröffentlichung in etwa 2-5 Jahren. Es heißt Projekt Valhalla.
Zabuzard

Antworten:

2

Wenn Sie Massenprimitive lesen, wie es Ihr Code anscheinend anzeigt, werden durch die Verwendung von ByteBuffer- Methoden wie asDoubleBuffer () oder asShortBuffer () einige der Arbeiten der untersten Ebene ausgelagert .

Beispiel:

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

(Code kompiliert, aber nicht getestet!)

Markierungsraum
quelle
0

Eine Möglichkeit, die zu einer Leistungsbeeinträchtigung führt, besteht darin java.lang.reflect.Array, das Array als Objekt zu behandeln, das dann die Wiederverwendung des gleichen Codes für alle Lesemethoden ermöglicht.

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

Die Duplizierung wurde auf Kosten einer gewissen Leistung, eines geringfügigen Mangels an Sicherheit vom Typ zur Kompilierungszeit und der Verwendung von Reflexion behoben.

Dave Jarvis
quelle