web-dev-qa-db-fra.com

Méthode Java: recherche d'un objet dans la liste d'un tableau à partir d'une valeur d'attribut connue

J'ai quelques questions en fait.

J'ai une classe Dog avec les champs d'instance suivants:

private int id;
private int id_mother;
private int id_father;
private String name="";
private String owner="";
private String bDate="";

J'ai aussi une classe Archive qui peut instancier Dog et mettre des objets Dog dans une ArrayList.

J'essaie d'écrire une méthode dans Archive qui prend un entier en tant qu'ID, recherche dans ArrayList et renvoie l'objet contenant cet ID.

private Dog getDog(int id){
    Dog dog = new Dog();
    int length=getSize();
    int i=0;

    dog=al.get(i);
    i++;

    while(dog.getId()!=id && i<length)
        dog=al.get(i);
        i++;

    if(dog.getId()!=id)
        dog=null;
    return dog;
}//end getDog

Il y a deux problèmes avec cette méthode (les autres méthodes que j'utilise fonctionnent). Tout d'abord, cela ne fonctionne pas et je ne vois pas pourquoi. Je suis en train de parcourir (potentiellement) tous les objets de la liste, une fois celle-ci terminée, en vérifiant si la boucle s'est terminée parce qu'il n'y avait plus d'objets à parcourir ou s'il avait trouvé un objet avec l'identifiant donné . Deuxièmement, cela semble être un processus extrêmement fastidieux. Y a-t-il un moyen d'accélérer le processus?

21
Northener

Un while s'applique à l'expression ou au bloc après le while.

Vous n'avez pas de bloc, donc votre tout se termine par l'expression dog=al.get(i);

while(dog.getId()!=id && i<length)
                dog=al.get(i);

Tout ce qui suit ne se produit qu'une fois.

Il n'y a aucune raison de créer un nouveau chien, car vous n'utilisez jamais le chien que vous avez découvert; vous affectez immédiatement un chien du tableau à votre référence de chien.

Et si vous devez obtenir une valeur pour une clé, vous devez utiliser une carte, pas un tableau.

Edit: cela n'a pas été modifié pourquoi?

Commentaire de l'OP:

Une autre question à propos de ne pas avoir à faire une nouvelle instance d'un chien. Si je sors juste des copies des objets de la liste de tableaux, comment puis-je les extraire de la liste de tableaux sans avoir d'objet dans lequel je le mette? Je viens de remarquer aussi que je ne suis pas entre parenthèses dans la boucle while.

Une référence Java et l'objet auquel elle fait référence sont deux choses différentes. Ils ressemblent beaucoup à une référence et à un objet C++, bien qu'une référence Java puisse être redirigée comme un pointeur C++.

Le résultat est que Dog dog; ou Dog dog = null vous donne une référence qui ne pointe vers aucun objet. new Dog() crée un objet sur lequel on peut pointer.

Suivre ensuite avec un dog = al.get(i) signifie que la référence pointe maintenant vers la référence de chien retournée par al.get(i). Comprenez, en Java, les objets ne sont jamais renvoyés, uniquement des références à des objets (qui sont les adresses de l'objet en mémoire). 

Le pointeur/référence/adresse du chien que vous avez créé est maintenant perdu, car aucun code n'y fait référence, car le référent a été remplacé par le référent que vous avez obtenu de al.get(). Finalement, le ramasse-miettes Java va détruire cet objet; en C++, vous auriez "perdu" de la mémoire.

Le résultat est que vous devez créer une variable pouvant faire référence à un chien. vous n'avez pas besoin de créer un chien avec new

(En réalité, vous n'avez pas besoin de créer une référence, car vous devez réellement renvoyer ce qu'une carte renvoie de sa fonction get (). Si la carte n'est pas paramétrée sur Dog, comme ceci: Map<Dog>, vous aurez besoin de convertir le retour de get, mais vous n'aurez pas besoin d'une référence: return (Dog) map.get(id); ou si la map est paramétrée, return map.get(id). la plupart des cas.)

16
tpdi

En supposant que vous ayez correctement écrit une méthode equals pour Dog qui compare, en fonction de l'id du Dog, le moyen le plus simple et le plus simple de retourner un élément de la liste est le suivant.

if (dogList.contains(dog)) {
   return dogList.get(dogList.indexOf(dog));
}

C'est moins intensif en performance que d'autres approches ici. Vous n'avez pas besoin de boucle du tout dans ce cas. J'espère que cela t'aides.

P.S Vous pouvez utiliser Apache Commons Lang pour écrire une méthode simple d'égalité pour Dog comme suit:

@Override
public boolean equals(Object obj) {     
   EqualsBuilder builder = new EqualsBuilder().append(this.getId(), obj.getId());               
   return builder.isEquals();
}
44
Jon

Pour améliorer les performances de l'opération, si vous souhaitez toujours rechercher des objets à l'aide d'un identificateur unique, vous pouvez envisager d'utiliser un Map<Integer,Dog> . Cela fournira une recherche à temps constant par clé. Vous pouvez toujours parcourir les objets eux-mêmes à l'aide de la fonction values() de la carte.

Un fragment de code rapide pour vous aider à démarrer:

// Populate the map
Map<Integer,Dog> dogs = new HashMap<Integer,Dog>();
for( Dog dog : /* dog source */ ) {
    dogs.put( dog.getId(), dog );
}

// Perform a lookup
Dog dog = dogs.get( id );

Cela aidera à accélérer un peu les choses si vous effectuez plusieurs recherches de même nature dans la liste. Si vous ne faites que la recherche, vous allez subir la même surcharge de boucle, peu importe.

16
Rob

Vous devez parcourir tout le tableau, cela ne changera pas. Vous pouvez cependant le faire un peu plus facilement

for (Dog dog : list) {
  if (dog.getId() == id) {
    return dog; //gotcha!
  }
}
return null; // dog not found.

ou sans la nouvelle boucle for

for (int i = 0; i < list.size(); i++) {
  if (list.get(i).getId() == id) {
    return list.get(i);
  }
}
13
Jorn

J'ai été intéressé de voir que l'affiche originale utilisait un style qui évitait les sorties prématurées. Entrée unique; Single Exit (SESE) est un style intéressant que je n’ai pas vraiment exploré. Il est tard et j'ai une bouteille de cidre, alors j'ai écrit une solution (non testée) sans sortie anticipée.

J'aurais dû utiliser un itérateur. Malheureusement, Java.util.Iterator a un effet secondaire dans la méthode get. (Je n'aime pas le design Iterator en raison de ses ramifications exceptionnelles.)

private Dog findDog(int id) {
    int i = 0;
    for (; i!=dogs.length() && dogs.get(i).getID()!=id; ++i) {
        ;
    }

    return i!=dogs.length() ? dogs.get(i) : null;
}

Notez la duplication de l'expression i!=dogs.length() (aurait pu choisir dogs.get(i).getID()!=id).

1

Si vous devez obtenir un attribut qui ne soit pas l'ID. Je voudrais utiliser CollectionUtils .

Dog someDog = new Dog();
Dog dog = CollectionUtils(dogList, new Predicate() {

@Override
public boolean evaluate(Object o)
{
    Dog d = (Dog)o;
    return someDog.getName().equals(d.getName());
}
});
0
Seth

J'ai résolu ce problème avec Java 8 lambdas

int dogId = 2;

return dogList.stream().filter(dog-> dogId == dog.getId()).collect(Collectors.toList()).get(0);
0
Federico Traiman