w Javie, EnumSet przechowuje elementy w nim zawarte w maską bitów/bitowym wektorem za pomocą long
(RegularEnumSet
) lub long[]
(JumboEnumSet
). Natrafiłem teraz na przypadek użycia, w którym mam wiele tysięcy obiektów domenowych (nazwijmy je Node
), z których każdy pokaże wszystkie elementy enum (nazwijmy to Flag
) w kolejności, która będzie się różnić w zależności od Obiektu.Store porządkuje wyliczenia w Javie
Obecnie przechowuję Zakon jako Guava ImmutableSet
, ponieważ gwarantuje to zachowanie zamówienia reklamowego. Jednak użyłem the methods explained on this page do porównania użycia pamięci w EnumSet<Flag>
, ImmutableSet<Flag>
i Flag[]
. Oto wyniki, gdy a) Flaga ma 64 elementów typu wyliczeniowego oraz b) wszystkie trzy warianty zawierają wszystkie 64 pozycje:
EnumSet: 32 bajtów
ImmutableSet: 832 bajtów
tablicy: 272 bajtów
Moje pytanie brzmi: czy istnieje sprytny sposób na spakowanie uporządkowania wyliczeniowego w wartość liczbową, aby uzyskać ślad pamięci mniejszy niż tablica? Jeśli to robi różnicę: w moim przypadku użycia zakładam, że zamówienie zawsze zawiera wszystkie elementy Enum.
Aby wyjaśnić: moje wyliczenie jest znacznie mniejsze niż to i nie mam żadnych problemów z pamięcią jak na razie, ani nie jest prawdopodobne, że ta sytuacja kiedykolwiek spowoduje problemy z pamięcią. Tyle tylko, że ta nieskuteczność budzi mnie, nawet na tym mikroskopijnym poziomie.
Aktualizacja:
Po sugestii różnych odpowiedzi i komentarze wymyśliłem tej struktury danych, który wykorzystuje tablicę bajtów. Zastrzeżenie: Nie implementuje interfejsu Set (nie sprawdza unikatowych wartości) i nie będzie skalowany do dużych wartości wyliczeniowych poza to, co bajt może pomieścić. Ponadto, złożoność jest straszne, bo Enum.values () musi być wielokrotnie (see here for a discussion of this problem) zapytaliśmy, ale tu idzie:
public class EnumOrdering<E extends Enum<E>> implements Iterable<E> {
private final Class<E> type;
private final byte[] order;
public EnumOrdering(final Class<E> type, final Collection<E> order) {
this.type = type;
this.order = new byte[order.size()];
int offset = 0;
for (final E item : order) {
this.order[offset++] = (byte) item.ordinal();
}
}
@Override
public Iterator<E> iterator() {
return new AbstractIterator<E>() {
private int offset = -1;
private final E[] enumConstants = type.getEnumConstants();
@Override
protected E computeNext() {
if (offset < order.length - 1) {
return enumConstants[order[++offset]];
}
return endOfData();
}
};
}
}
Ślad pamięci:
EnumOrdering: 104
To całkiem niezły wynik do tej pory, dzięki bestsss i JB Nizet!
Aktualizacja: Zmieniłem kod tylko do wprowadzania iterable, bo nic innego wymagałaby sensownych implementacje dla równymi/hashCode/zawiera itp
prosta tablica bajtów [] zrobi, bajt [] zawiera enum.ordinal. jeśli masz więcej niż 256 pozycji, możesz użyć skrótu []/int []. Możesz również spakować przedmioty na mniej niż 8 bitów. Być może będziesz musiał zająć się serializacją, tak czy inaczej kod będzie mniejszy niż 200 linii i to całkiem banalne. – bestsss
jeśli nie potrzebujesz zamówienia reklamowego, po prostu użyj pojedynczego długiego - może zawierać do wyliczenia w/64 elementów, tak jak to zrobiono w C. – bestsss
@bestsss, jeśli nie potrzebowałem zamówienia reklamowego. EnumSet, który robi dokładnie to, co –