2017-05-13 3 views
8

J'ai une classe IndexEntry qui ressemble à ceci:Comment obtenir le mot le plus fréquent dans une carte et sa fréquence d'occurrence correspondante en utilisant des flux Java 8?

public class IndexEntry implements Comparable<IndexEntry> 
{ 
    private String word; 
    private int frequency; 
    private int documentId; 
    ... 
    //Simple getters for all properties 
    public int getFrequency() 
    { 
     return frequency; 
    } 
    ... 
} 

je stocke des objets de cette classe dans une goyave SortedSetMultimap (ce qui permet de multiples valeurs par clé) où je délimiterai un mot String à quelques IndexEntry s . Dans les coulisses, il met en correspondance chaque mot avec un SortedSet<IndexEntry>. J'essaye de mettre en place une sorte de structure indexée des mots aux documents et de leurs fréquences d'occurrence à l'intérieur des documents.

Je sais comment obtenir le compter du mot le plus commun, mais je ne peux pas sembler obtenir le mot lui-même.

Voici ce que je dois obtenir le compte du terme le plus commun, où entries est le SortedSetMultimap, ainsi que des méthodes d'aide:

public int mostFrequentWordFrequency() 
{ 
    return entries 
      .keySet() 
      .stream() 
      .map(this::totalFrequencyOfWord) 
      .max(Comparator.naturalOrder()).orElse(0); 
} 

public int totalFrequencyOfWord(String word) 
{ 
    return getEntriesOfWord(word) 
      .stream() 
      .mapToInt(IndexEntry::getFrequency) 
      .sum(); 
} 

public SortedSet<IndexEntry> getEntriesOfWord(String word) 
{ 
    return entries.get(word); 
} 

Je suis en train d'apprendre Java 8 fonctionnalités, car ils semblent vraiment utiles . Cependant, je n'arrive pas à faire fonctionner le flux comme je le souhaite. Je veux être en mesure d'avoir à la fois le mot et sa fréquence à la fin du flux, mais sauf cela, si j'ai le mot, je peux très facilement obtenir le nombre total d'occurrences de ce mot.

Actuellement, je continue à me retrouver avec un Stream<SortedSet<IndexEntry>>, avec lequel je ne peux rien faire. Je ne sais pas comment obtenir le mot le plus fréquent sans les fréquences, mais si j'ai la fréquence, je n'arrive pas à suivre le mot correspondant. J'ai essayé de créer une classe WordFrequencyPair POJO pour stocker les deux, mais ensuite j'ai juste eu un Stream<SortedSet<WordFrequencyPair>>, et je ne pouvais pas comprendre comment mapper cela en quelque chose d'utile.

Qu'est-ce qui me manque?

Répondre

6

Je pense que ce serait une meilleure conception d'utiliser la documentId comme la clé de la TreeMultimap plutôt que le word:

import com.google.common.collect.*; 

public class Main { 

    TreeMultimap<Integer, IndexEntry> entries = TreeMultimap.<Integer, IndexEntry>create(Ordering.arbitrary(), Ordering.natural().reverse()); 

    public static void main(String[] args) { 
     // Add elements to `entries` 

     // Get the most frequent word in document #1 
     String mostFrequentWord = entries.get(1).first().getWord(); 
    } 

} 

class IndexEntry implements Comparable<IndexEntry> { 

    private String word; 

    private int frequency; 

    private int documentId; 

    public String getWord() { 
     return word; 
    } 

    public int getFrequency() { 
     return frequency; 
    } 

    public int getDocumentId() { 
     return documentId; 
    } 

    @Override 
    public int compareTo(IndexEntry i) { 
     return Integer.compare(frequency, i.frequency); 
    } 

} 

Vous pouvez ensuite mettre en œuvre les méthodes que vous aviez avant ce qui suit:

public static int totalFrequencyOfWord(String word) { 
    return entries.values() 
        .stream() 
        .filter(i -> word.equals(i.getWord())) 
        .mapToInt(IndexEntry::getFrequency) 
        .sum(); 
} 

/** 
* This method iterates through the values of the {@link TreeMultimap}, 
* searching for {@link IndexEntry} objects which have their {@code word} 
* field equal to the parameter, word. 
* 
* @param word 
*  The word to search for in every document. 
* @return 
*  A {@link List<Pair<Integer, Integer>>} where each {@link Pair<>} 
*  will hold the document's ID as its first element and the frequency 
*  of the word in the document as its second element. 
* 
* Note that the {@link Pair} object is defined in javafx.util.Pair 
*/ 
public static List<Pair<Integer, Integer>> totalWordUses(String word) { 
    return entries.values() 
        .stream() 
        .filter(i -> word.equals(i.getWord())) 
        .map(i -> new Pair<>(i.getDocumentId(), i.getFrequency())) 
        .collect(Collectors.toList()); 
} 
+0

Ainsi, supposons que je devais voir tous les documents ids qu'un mot donné était présent dans, ainsi que le nombre d'occurrences dans ce document. Dans votre exemple, comment ferais-je cela? –

+0

Je vais écrire une méthode pour le faire et expliquer comment cela fonctionne –

+0

Je pense que cela va fonctionner. Merci! –

0

solution native par JDK:

entries.keySet().stream() 
    .collect(groupingBy(IndexEntry::getWord, summingInt(IndexEntry::getFrequency))) 
    .values().stream().max(Comparator.naturalOrder()).orElse(0L); 

Ou par StreamEx

StreamEx.of(entries.keySet()) 
    .groupingBy(IndexEntry::getWord, summingInt(IndexEntry::getFrequency)) 
    .values().stream().max(Comparator.naturalOrder()).orElse(0L);