2009-09-30 8 views
1

J'ai une application Java qui utilise JPA.Comment gérer au mieux la liste <Entity>, la liste <Entity Id>, la liste <Entity name> efficacement?

Dire que j'ai une entité appelée Product avec name et price attributs (toutes entités ont un attribut id).

Naturellement je peux obtenir un List<Product> assez facilement (d'une requête ou d'une autre entité), mais souvent je veux une List<String> (liste des noms de produits) ou List<Long> (liste des prix des produits ou liste des ids de produits).

Beaucoup de temps, il est tout aussi facile de passer toute Product autour, mais il y a deux cas où je ne veux pas faire:

  • Je passe la liste à une classe qui ne devrait pas dépendre de la classe Product.
  • Il est nettement plus facile/plus rapide d'obtenir une liste d'identifiants que des objets de produit complets (mais dans certains cas je les ai déjà).

La façon naïve de faire cela irait quelque chose comme:

List<Long> productIds = new ArrayList<Long>(); 
for(Product product: products) { 
    productIds.add(product.getId()); 
} 

Mais je ne l'aime pas parce qu'il est désordonné et inefficace. En python je ferais quelque chose comme:

[ p.id for p in products ] 

Le "meilleur" que je peux venir avec en Java est:

public class ProductIdList extends AbstractList<Long> { 
    private List<Product> data; 

    public ProductIdList(List<Product> data) { 
    this.data = data; 
    } 

    public Long get(int i) { 
    return data.get(i).getId(); 
    } 

    public int size() { 
    return data.size(); 
    } 

    public Long remove(int i) { 
    return data.remove(i).getId(); 
    } 

    /* For better performance */ 
    public void clear() { 
    data.clear(); 
    } 

    /* Other operations unsupported */ 
} 

Plus:

  • Cette approche n'a pas besoin de copier les données
  • Il s'agit d'une véritable "vue" sur les données - les modifications apportées à la liste sous-jacente sont reflétées.

Moins:

  • On dirait que beaucoup de code
  • Besoin d'une classe comme ceci pour chaque attribut Je veux accéder comme ça

Alors est-ce un bon idée ou pas? Devrais-je simplement créer des listes secondaires la plupart du temps? Y a-t-il une troisième option que je n'ai pas envisagée?

+0

Mettre un commentaire non une réponse. Juste un petit conseil, vous pouvez toujours aller chercher les identifiants de produits seulement, pas tout le produit (en utilisant ce que j'ai entendu s'appeler une requête scalaire). Par exemple. SELECT prod.id FROM Product prod ... –

+0

Exactement. Peut-être aurais-je dû être plus clair. Parfois, je peux faire exactement cela, et il n'y a pas de problème. Parfois, j'ai déjà une liste et je ne veux pas de requery. – Draemon

+0

Vous pouvez utiliser une classe qui a une méthode pour chaque attribut. Cela réduirait un CONS au moins. – StampedeXV

Répondre

1

Vous pouvez réduire la taille de ces classes d'emballage par attribut en les faisant hériter d'une classe abstraite qui a une méthode getAttribute.Quelque chose comme ceci:

public abstract class AbstractProductList<T> extends AbstractList<T> { 
    protected List<Product> data; 

    public AbstractProductList(List<Product> data) { 
    this.data = data; 
    } 

    protected abstract T getAttribute(Product product); 

    public T get(int i) { 
    return getAttribute(data.get(i)); 
    } 

    public int size() { 
    return data.size(); 
    } 

    public T remove(int i) { 
    return getAttribute(data.remove(i)); 
    } 

    /* For better performance */ 
    public void clear() { 
    data.clear(); 
    } 

    /* Other operations unsupported */ 
} 

public class ProductIdList extends AbstractProductList<Long> { 
    public ProductIdList(List<Product> data) { 
    super(data); 
    } 

    protected Long getAttribute(Product product) { 
    return product.getId(); 
    } 
} 
+0

J'aime ça. Je pense que la classe Product pourrait aussi être générique. – Draemon

0

Vous pouvez utiliser une classe qui a une méthode pour chaque attribut. Cela réduirait un CONS au moins.

Ceci est votre exemple à l'écoute de ma proposition.

public class ProductList { 
    private List<Product> data; 

    public ProductIdList(List<Product> data) { 
    this.data = data; 
    } 

    public Long getId(int i) { 
    return data.get(i).getId(); 
    } 

    public List<String> getProductNames(int i) { 
    return data.get(i).getProductNames(); 
    } 

    public Product getProduct(int i) { 
    return data.get(i); 
    } 

    public int size() { 
    return data.size(); 
    } 

    public Long remove(int i) { 
    return data.remove(i).getId(); 
    } 

    /* For better performance */ 
    public void clear() { 
    data.clear(); 
    } 
} 
+0

Cela n'implémente pas la liste, donc il ne peut pas être utilisé où une liste est attendue. – Draemon

3

juste jeter dans une autre possibilité, vous pouvez utiliser Transformer Apache Commons et recueillir pour créer une nouvelle collection ou transformer pour modifier l'existant:

Collection<Long> productIds = CollectionUtils.collect(products, new Transformer() { 
     public Long transform(Object o) { 
      return ((Product) o).id; 
     } 
    }); 
+0

C'est probablement le moyen le plus correct pour implémenter exactement ce que je veux. Il est malheureux que Commons n'ait pas rattrapé les génériques, alors que la bibliothèque supplémentaire signifie que je ne peux pas vraiment le justifier. Toujours, +1 – Draemon

Questions connexes