236 votes

Comment compter le nombre d'occurrences d'un élément dans une liste ?

J'ai un ArrayList une classe de collection de Java, comme suit :

ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");

Comme vous pouvez le voir, le animals ArrayList se compose de 3 bat et un owl élément. Je me demandais s'il existe une API dans le cadre de Collection qui renvoie le nombre de bat ou s'il existe un autre moyen de déterminer le nombre d'occurrences.

J'ai découvert que la collection de Google Multiset dispose d'une API qui renvoie le nombre total d'occurrences d'un élément. Mais elle n'est compatible qu'avec le JDK 1.5. Notre produit est actuellement dans le JDK 1.6, je ne peux donc pas l'utiliser.

0 votes

C'est l'une des raisons pour lesquelles vous devriez programmer en fonction d'une interface plutôt que d'une implémentation. Si vous trouvez la bonne collection, vous devrez modifier le type pour utiliser cette collection. Je vais poster une réponse à ce sujet.

427voto

Lars Andren Points 2902

Je suis sûr que la méthode de la fréquence statique dans Collections serait utile ici :

int occurrences = Collections.frequency(animals, "bat");

C'est comme ça que je le ferais de toute façon. Je suis presque sûr que c'est la version 1.6 de JDK.

1 votes

Préférez toujours les Api de JRE, qui ajoutent une autre dépendance au projet. Et ne réinventez pas la roue ! !

5 votes

Il a été introduit dans le JDK 5 (bien que personne n'utilise une version antérieure à celle-ci, donc cela n'a pas d'importance). docs.oracle.com/javase/8/docs/technotes/guides/collections/

23voto

OscarRyz Points 82553

Cela montre pourquoi il est important de " Faire référence aux objets par leurs interfaces "comme décrit dans Java efficace livre.

Si vous codez en fonction de l'implémentation et utilisez ArrayList à, disons, 50 endroits dans votre code, lorsque vous trouverez une bonne implémentation de "liste" qui compte les éléments, vous devrez changer ces 50 endroits, et probablement casser votre code (si vous êtes le seul à l'utiliser, ce n'est pas un problème, mais si quelqu'un d'autre l'utilise, vous casserez aussi son code).

En programmant l'interface, vous pouvez laisser ces 50 emplacements inchangés et remplacer l'implémentation de ArrayList par "CountItemsList" (par exemple) ou une autre classe.

Vous trouverez ci-dessous un exemple très simple de rédaction. Ce n'est qu'un exemple, une liste prête pour la production serait beaucoup plus compliqué.

import java.util.*;

public class CountItemsList<E> extends ArrayList<E> { 

    // This is private. It is not visible from outside.
    private Map<E,Integer> count = new HashMap<E,Integer>();

    // There are several entry points to this class
    // this is just to show one of them.
    public boolean add( E element  ) { 
        if( !count.containsKey( element ) ){
            count.put( element, 1 );
        } else { 
            count.put( element, count.get( element ) + 1 );
        }
        return super.add( element );
    }

    // This method belongs to CountItemList interface ( or class ) 
    // to used you have to cast.
    public int getCount( E element ) { 
        if( ! count.containsKey( element ) ) {
            return 0;
        }
        return count.get( element );
    }

    public static void main( String [] args ) { 
        List<String> animals = new CountItemsList<String>();
        animals.add("bat");
        animals.add("owl");
        animals.add("bat");
        animals.add("bat");

        System.out.println( (( CountItemsList<String> )animals).getCount( "bat" ));
    }
}

Principes OO appliqués ici : héritage, polymorphisme, abstraction, encapsulation.

13 votes

Il faut toujours essayer la composition plutôt que l'héritage. Votre implémentation est maintenant bloquée sur ArrayList alors qu'il peut y avoir des moments où vous voulez une LinkedList ou autre. Votre exemple aurait dû prendre une autre LinkedList dans son constructeur/factory et retourner un wrapper.

0 votes

Je suis tout à fait d'accord avec vous. La raison pour laquelle j'ai utilisé l'héritage dans l'exemple est qu'il est beaucoup plus facile de montrer un exemple de fonctionnement en utilisant l'héritage que la composition (avoir à implémenter l'interface List). L'héritage crée le couplage le plus élevé.

3 votes

Mais en l'appelant CountItemsList, vous sous-entendez qu'il fait deux choses : il compte les éléments et c'est une liste. Je pense qu'une seule responsabilité pour cette classe, compter les occurrences, serait aussi simple et vous n'auriez pas besoin d'implémenter l'interface List.

13voto

Ray Hidayat Points 7961

Désolé, il n'y a pas de méthode simple qui puisse le faire. Tout ce que vous devez faire, c'est créer une carte et compter les fréquences avec elle.

HashMap<String,int> frequencymap = new HashMap<String,int>();
foreach(String a in animals) {
  if(frequencymap.containsKey(a)) {
    frequencymap.put(a, frequencymap.get(a)+1);
  }
  else{ frequencymap.put(a, 1); }
}

2 votes

Ce n'est vraiment pas une solution évolutive - imaginez que l'ensemble de données de MM comprenne des centaines et des milliers d'entrées et que MM veuille connaître les fréquences de chaque entrée. Cette tâche pourrait s'avérer très coûteuse, d'autant plus qu'il existe de bien meilleures façons de procéder.

0 votes

Oui, ce n'est peut-être pas une bonne solution, mais cela ne veut pas dire qu'elle est mauvaise.

0 votes

Il veut juste le nombre d'occurrences de "bat". Il suffit d'itérer une fois sur la liste originale de tableaux et d'incrémenter un compteur chaque fois que vous voyez 'bat'.

11voto

Kevin Points 19613

Il n'existe pas de méthode native en Java pour le faire à votre place. Cependant, vous pouvez utiliser IterableUtils#countMatches() d'Apache Commons-Collections pour le faire à votre place.

1 votes

Référez-vous à ma réponse ci-dessous - la bonne réponse est d'utiliser une structure qui soutient l'idée de comptage dès le début plutôt que de compter les entrées du début à la fin chaque fois qu'une requête est faite.

0 votes

@mP Donc vous downvotez tous ceux qui ont une opinion différente de la vôtre ? Et s'il ne peut pas utiliser un sac pour une raison quelconque ou s'il est obligé d'utiliser l'une des collections natives ?

0 votes

-1 pour être un mauvais perdant :-) Je pense que mP t'a rétrogradé parce que ta solution coûte du temps à chaque fois que tu veux un résultat. Un sac ne coûte un peu de temps qu'à l'insertion. Comme les bases de données, ce genre de structures a tendance à être "plus lues qu'écrites", il est donc logique d'utiliser l'option la moins coûteuse.

8voto

Adeel Ansari Points 24434

Je me demande pourquoi vous ne pouvez pas utiliser l'API Collection de Google avec le JDK 1.6. Est-ce que c'est écrit ? Je pense que vous pouvez, il ne devrait pas y avoir de problèmes de compatibilité, car il est construit pour une version inférieure. Le cas aurait été différent si elle avait été construite pour la version 1.6 et que vous exécutiez la version 1.5.

Est-ce que je me trompe quelque part ?

0 votes

Ils ont clairement indiqué qu'ils sont en train de mettre à niveau leur API vers JDK 1.6.

1 votes

Cela ne rend pas les anciens incompatibles. Si ?

0 votes

Il ne devrait pas. Mais la façon dont ils ont jeté des avertissements, me met mal à l'aise pour l'utiliser dans leur version 0.9.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X