2010-04-22 13 views
8

Stavo cercando un'implementazione decente di un'implementazione di lista pigro non modificabile generica per includere le voci dei risultati di ricerca. La parte non modificabile del compito è facile in quanto può essere raggiunto da Collections.unmodifiableList() quindi ho solo bisogno di risolvere la parte pigro.Elenco pigro non modificabile in Google Collections

Sorprendentemente, google-collections non ha nulla da offrire; mentre LazyList from Apache Commons Collections non supporta i generici.

ho trovato an attempt di costruire qualcosa sulla parte superiore del google-collezioni, ma sembra essere incompleta (ad esempio non supporta size()), obsoleto (non viene compilato con 1.0 finale) e che richiedono alcune classi esterni, ma potrebbe essere utilizzato come buon punto di partenza per costruire la mia classe.

Qualcuno è a conoscenza di una buona implementazione di LazyList? In caso contrario, quale opzione pensi sia meglio:

  • scrivere la mia implementazione, basata su google-collections ForwardingList, simile a ciò che faceva Peter Maas;
  • scrivere il proprio wrapper attorno a Commons Collections LazyList (il wrapper aggiungerebbe solo generici quindi non devo eseguire il cast ovunque, ma solo nel wrapper stesso);
  • basta scrivere qualcosa sopra java.util.AbstractList;

Qualsiasi altro suggerimento è benvenuto.

MODIFICA: spiegazione perché ho bisogno di una lista pigra.

Ho ottenuto un risultato di ricerca Lucene (TopDocs) che è fondamentalmente un mucchio di indicazioni per i documenti Lucene. La mia classe dei risultati di ricerca prenderebbe questi suggerimenti come input e restituirebbe un elenco di oggetti che sono fatti di documenti Lucene estratti o elaborati in altro modo. Inserendo tutto in una lista pigra, voglio assicurarmi di non eseguire elaborazioni costose quando non necessario.

+0

Puoi dire un po 'di più su cosa stai cercando di fare, in modo che possiamo capire perché una "lista pigra" è necessariamente l'approccio giusto? –

+0

@Kevin ha appena aggiunto la spiegazione, grazie! – mindas

risposta

4

Ho effettivamente risolto questo in un modo diverso. Invece di passare attraverso pigro e immodificabile, ho semplicemente implementato java.lang.Iterable<T>. L'implementazione genera UnsupportedOperationException su remove().

Ho dovuto modificare leggermente alcune altre parti di codice, rinunciare a qualcosa, ma credo che questa fosse la scelta migliore. Iterable consente l'inserimento del ciclo foreach.

Ci scusiamo se non sarà una scelta praticabile per qualcuno in una situazione simile e grazie mille per le idee.

2

La soluzione di Peter Maas si collega mi sembra buona - vi consiglio caldamente di lavorare con questo, invece di passare del tempo a reinventarlo. Basta sostituire Factory<T> con Supplier<T> (incluso nelle raccolte Google). Anche la sua implementazione di subList è abbastanza intelligente, sebbene abbia alcune implicazioni particolari: se si ottiene un subList() e si prova ad aggiungere un elemento dai limiti della sottocartella, non si otterrà un IndexOutOfBoundsException (come dovrebbe fare una lista secondaria corretta) , ma inserirai ulteriori elementi nell'elenco. Le probabilità sono che non avresti bisogno di sottoliste, quindi il più sicuro sarebbe implementare tale metodo lanciando UnsupportedOperationException (o costruisci un LazyList che ha un flag in più se è permesso di crescere di inversioni get() oltre le sue dimensioni: se creato da subList, quindi non lo è).

size()è supportato (automaticamente dallo ForwardingList stesso).

Aggiornamento: Nota che, come ha detto Kevin, non hai spiegato perché qualcosa del genere sarebbe davvero ciò di cui hai bisogno. Inoltre, forse si potrebbe prendere in considerazione se qualcosa come questo potrebbe essere applicabile:

final Supplier<T> supplier = ...; 
Map<Integer, T> graphs = new MapMaker() 
    .makeComputingMap(
     new Function<Integer, T>() { 
     public T apply(Integer index) { 
      return supplier.get(); 
     } 
     }); 

Dal List<T> e Map<Integer, T> più o meno rappresentare lo stesso tipo di dato astratto, e dal momento che appare dal commento che (1) don Mi piacciono i null da considerare come elementi (bene!), e (2) che la tua struttura potrebbe essere scarsa, in cui una lista di array effettiva sarebbe dispendiosa.

+0

Quello che non mi piace di LazyList() di Peter Maas è che restituisce il numero di articoli già inizializzati e non il numero di articoli eventualmente disponibili. Citando il suo Javadoc: "Quando il metodo {@link #get (int)} viene chiamato con un indice maggiore della dimensione dell'elenco, l'elenco aumenterà automaticamente di dimensione e restituirà un nuovo oggetto dal factory specificato". Preferirei avere questo abstract in modo che le sottoclassi sarebbero costrette a fornire la dimensione dell'elenco in quanto potrebbe essere impossibile in un'implementazione generica conoscerla. E sì, ho bisogno anche del supporto per il sottotitolo. – mindas

+0

@mindas, se questa è la caratteristica che ti impedisce di utilizzare l'implementazione, ti suggerisco di eseguire il rollover prendendo il migliore con una licenza compatibile e modificando le dimensioni in base alle tue esigenze. – Yishai

4

C'è un progetto che ha aggiunto Generics dotato di Apache Commons-collezioni:

http://sourceforge.net/projects/collections/

(Commons-collezioni con i generici)

+0

Grazie per questo! Ho comunque capito che le commons-collections soffrono dello stesso problema di dimensioni() (vedi il mio commento per Dimitris). Questo può, tuttavia, essere utile per altre persone. – mindas

5

Google-collections e il metodo Lists.transform di Guava ti danno la pigrizia che cerchi. Rimanere con Iterables.transform dovrebbe essere altrettanto buono.

Ma, se sei anche preoccupato che i risultati dovrebbero essere memorizzati nella cache una volta creati per la prima volta, beh ... in questo momento, questo è il migliore che mi viene in mente, e non sarà molto confortante:

List<Supplier<ExpensiveResult>> suppliers = 
    ImmutableList.copyOf(Lists.transform(keys, 
     new Function<Key, Supplier<ExpensiveResult>>() { 
      public Supplier<ExpensiveResult> apply(Key key) { 
      return Suppliers.memoize(Suppliers.compose(
       myExpensiveFunction(), 
       Suppliers.ofInstance(key))); 
      } 
     })); 

return Lists.transform(suppliers, ThisClass.<ExpensiveResult>supplyFunction()); 

. . . 

private static <T> Function<Supplier<T>, T> supplyFunction() { 
    return new Function<Supplier<T>, T>() { 
    public T apply(Supplier<T> supplier) { 
     return supplier.get(); 
    } 
    }; 
} 

Sì, puoi ridere. E probabilmente dovresti. Io ... non lo consiglio davvero. Ancora potrebbe essere meno codice di quello che stai facendo attualmente. E l'ho appena testato ... funziona.

+0

Grazie Kevin! Mi sto ancora trattenendo dall'usare Guava principalmente perché non è stato finalizzato (fondamentalmente gli stessi motivi per cui non hai un repository pubblico di Maven). Per quanto riguarda la tua soluzione ... immagino che non alzi nemmeno un sopracciglio di un appassionato di funzioni, ma sicuramente avrei molte cose da dire alla mia prossima confessione, una volta messo questo in! – mindas

+2

C'è stata una versione binaria per alcune settimane, e nessuna delle API menzionate sopra è nemmeno contrassegnata Beta (credo). –