2011-11-23 9 views
7

Mam obiekt Point:Złożony klucz do Cache guawy (zmiany biegów)

class Point { 
    final int x,y; 
    ... 
    } 

Ponieważ będzie używana te punkty/utworzonego w każdym miejscu w moim kodu, chcę zacząć korzystać z guawy cache. Niestety, CacheLoader akceptuje tylko jeden parametr. Another question tutaj na stackoverflow użyj obiektu pary dla podobnego problemu. Ale nie podoba mi się pomysł stworzenia obiektu obojętnego dla każdego żądania pamięci podręcznej. Więc wymyślam moje własne obejście:

Ponieważ obiekt jest określony przez xiy, myślę, że mogę scalić (przesunąć) obie wartości w długą, która będzie moim kluczem.

void test(int x, int y) { 
    Long key = (long) ((long) (x) << Integer.SIZE | y); 
    Point point = cache.get(key); 
} 

CacheLoader<Long, Point> loader = new CacheLoader<Long, Point>() { 
    public Point load(Long key) throws Exception { 
    final int x,y; 
     // shift magic 
     x = (int) (key >> Integer.SIZE); 
     y = key.intValue(); 
     return new Point(x, y); 
    } 
}; 

Jestem rzeczywiście noob shift. Czy to zadziała? Przegapiłem coś? Czy jest to "szybsze" niż klasa pary? To jest moje pytanie!

Tak, testuję kod i działa on do tej pory.

+3

Jezu. * Proszę * stworzyć prawdziwy obiekt (z dwoma dobrze nazwanymi polami) dla punktu! Już tworzysz obiekt "dummy", java.lang.Long, po prostu bardziej zaciemniony. –

Odpowiedz

7

Jak na ten temat? Twoja klasa Point musi poprawnie implementować equals() i hashcode().

static class Points { 
    static final Interner<Point> INTERNER = Interners.newStrongInterner(); 

    public static Point cached(final int x, final int y) { 
    return INTERNER.intern(new Point(x, y)); 
    } 
} 

Twoim prawdziwym celem było buforowanie równych obiektów, prawda? Niż to wystarczy do twoich potrzeb. Zastosowanie:

Point center = Points.cached(0, 0); 

Albo zmienioną wersję swojej przykład cache:

CacheLoader<Point, Point> loader = new CacheLoader<Point, Point>() { 
    @Override 
    public Point load(final Point point) { 
    return point; 
    } 
} 
... 
Point center = cache.get(new Point(0, 0)); 
+0

Hum ten Interner jest dla mnie nowy. Wygląda ciekawie. Dzięki! Oczywiście poprawnie implementuje się 'równy' i' hashcode'. Nie chcę, żeby pan Bloch wsadził mnie do piekła Javy :-D –

+4

Ouch. Bardzo zły pomysł. Po pierwsze, zamieniasz obiekty z niewielkim cyklem życia (kawałek dla wszystkich śmieciarek) w stałe obiekty, dzięki czemu wszystkie przyszłe pełne gc są wolniejsze. Następnie ta rzecz rośnie bez ograniczeń, niewiele różni się od wycieku pamięci. Po trzecie, aby uzyskać równomierne rozłożenie pamięci, * wszystkie zapisane w pamięci podręcznej punkty musiałyby zostać zachowane przynajmniej w dwóch miejscach *. Tylko wtedy, gdy zostaną ponownie użyte, będzie to jakikolwiek zapis pamięci. Nie mówiąc już o tym, że przedmioty te * nie * muszą być trzymane w pobliżu, inne niż dla poszukiwań, nie mówiąc już o trzymaniu ich w dwóch miejscach. Zastanowiłabym się usunąć tę odpowiedź: - \ –

+0

Masz rację. Implementacja StrongInterner będzie przechowywać punkty do końca. Interners.newWeakInterner() załatwiłoby sprawę. Korzyści są osiągane tylko wtedy, gdy punkty są naprawdę ponownie wykorzystane. – bjmi

2

Prawdopodobnie jest szybszy (jeśli różnica jest w ogóle możliwa do zmierzenia), ale klasa pary sprawi, że kod będzie znacznie lepszy, aby go zrozumieć lub wykorzystać ponownie. Poszedłbym z ogólną klasą Pair:

public final class Pair<L, R> { 

    private final L left; 
    private final R right; 

    private Pair(L left, R right) { 
     this.left = left; 
     this.right = right; 
    } 

    public static <L,R>Pair<L,R> of(L left, R right){ 
     return new Pair<L, R>(left,right); 
    } 

    @Override 
    public boolean equals(final Object obj) { 
     if (obj instanceof Pair) { 
      final Pair<?,?> other = (Pair<?,?>) obj; 
      return Objects.equal(left, other.left) 
       && Objects.equal(right, other.right); 
     } else { 
      return false; 
     } 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hashCode(left, right); 
    } 

    @Override 
    public String toString() { 
     return MoreObjects.toStringHelper(this) 
         .add("left", left) 
         .add("right", right) 
         .toString(); 
    } 

} 

Po znalezieniu tego w bazie kodu znajdziesz więcej niż jedno zastosowanie. Przesunięcie bitów nie jest podobne do Java (chociaż wielu innych się nie zgadza).

Guava docs

+0

OK, różnica może nie być mierzalna. Rozumiem! Ale jakie są wady, o których wspomniałeś ?! To był jeden z moich pytań. –

+1

@MarcelJaeschke za pomocą obiektów sprawia, że ​​twój kod jest czytelny, zrozumiały i możliwy do ponownego użycia. zmiana bitu nie działa. Jeśli pytasz mnie, to jest przedwczesna optymalizacja. –

+0

Tak, znam koncepcję OOP bardzo dobrze! Ale myślę, że twój sposób jest po prostu nadnaturalizacją! Punkt obiektu składa się tylko z dwóch pól int. Twoja klasa par ma te dwa pola! W rzeczywistości tworzysz kopię obiektu! Więc w rzeczywistości możesz użyć obiektu jako klucza w pierwszej kolejności! Mam na myśli (przepraszam za format): Punkt punktowy = nowy punkt (x, y); Point pointFromCache = cache.get (point); CacheLoader loader = new CacheLoader () { public Point load (Point key) zgłasza klucz powrotu Exception { ; } }; –