Jak uniknąć powielania kodu dotyczącego typów pierwotnych?

9

tło

Bitowy strumień wejściowy jest wspierany przez tablicę bajtów. Istnieje kilka metod, które odczytują z tej tablicy bajtów na różne wymuszone pierwotne tablice.

Problem

Istnieje zduplikowany kod. W Javie brakuje generycznych typów pierwotnych, więc być może powtórzenie jest nieuniknione.

Kod

Powtarzalny kod jest widoczny w następujących metodach:

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

Zwróć uwagę, jak final byte[] outodnosi się do readByte(bits)tak, jak final short[] outodnosi się do readShort(bits). Relacje te stanowią sedno problemu.

Pytanie

Jak można wyeliminować duplikację, jeśli w ogóle, bez znacznego spadku wydajności (np. Przez autoboxing)?

Związane z

Dave Jarvis
źródło
6
Nie, nic tam nie możesz zrobić. Duplikacja jest jedyną opcją.
Andy Turner
Skorzystaj z prymitywnej kolekcji innej firmy
Vince Emigh
1
Java lacks generics on primitive types, so perhaps the repetition is unavoidable.Tak. (Zwykle nie stanowi to większego problemu, ponieważ jeden program potrzebuje więcej niż kilku różnych prymitywów. Można to również „naprawić”, umieszczając prymitywy w klasie i stosując serializację obiektów, chociaż może to być stosunkowo wolne. )
markspace
3
Również (właśnie to zapamiętałem), jeśli czytasz zbiorcze operacje podstawowe, jak wydaje się to wskazywać Twój kod, przy użyciu ByteBuffermetod takich jak asDoubleBuffer()lub asShortBuffer()spowoduje odciążenie części pracy na najniższym poziomie. docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/…
markspace
1
Należy pamiętać, że podejmowane są pewne wysiłki w celu zapewnienia prymitywnej ogólnej obsługi języka Java, tj List<int>. Wydanie może za około 2-5 lat. Nazywa się Project Valhalla.
Zabuzard

Odpowiedzi:

2

Jeśli czytasz zbiorcze operacje podstawowe, jak wydaje się to wskazywać Twój kod, użycie metod ByteBuffer , takich jak asDoubleBuffer () lub asShortBuffer (), odciąży część pracy na najniższym poziomie.

Przykład:

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

(Kod kompiluje się, ale nie został przetestowany!)

markspace
źródło
0

Jedną z możliwości, która może spowodować obniżenie wydajności, jest java.lang.reflect.Arraytraktowanie tablicy jako obiektu, który następnie pozwala na ponowne użycie tego samego kodu we wszystkich metodach odczytu.

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

Problem duplikacji został rozwiązany kosztem pewnej wydajności, niewielkiego braku bezpieczeństwa typu kompilacji i użycia refleksji.

Dave Jarvis
źródło