Java TreeMap:从单个键检索多个值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13355721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:23:45  来源:igfitidea点击:

Java TreeMap: Retrieving multiple values from a single key

javakeytreemaptreeset

提问by King Triumph

I'm trying to retrieve multiple values from a single key in a TreeMap. The idea is that each key will link to multiple values and should be searchable. Right now the trouble I'm running into is that when I am only able to get one value back from the key.

我正在尝试从 TreeMap 中的单个键中检索多个值。这个想法是每个键都将链接到多个值并且应该是可搜索的。现在我遇到的麻烦是,当我只能从密钥中取回一个值时。

The key is a String and the value is a custom object, called Song. Song contains multiple elements. The goal is to extract the lyrics, word by word, from each song and use each word as a key. The key then links to each Song (the value) that contains the key.

键是一个字符串,值是一个名为 Song 的自定义对象。歌曲包含多种元素。目标是从每首歌曲中逐字提取歌词,并将每个单词用作关键字。然后该键链接到包含该键的每首歌曲(值)。

I've searched StackOverFlow and the web in general for tips, and I've seen a few, but nothing that directly addresses my stumbling block. One idea I saw was to change the value into some kind of array or list. I may try that tomorrow when my brain is refreshed.

我已经在 StackOverFlow 和网络上搜索了一般的技巧,我看到了一些,但没有直接解决我的绊脚石。我看到的一个想法是将值更改为某种数组或列表。明天当我的大脑恢复活力时,我可能会尝试。

Anyway, thanks in advance for any tips and advice. And yes, this is homework. No, I did not tag is because I was informed that the homework tag is no longer commonly in use.

无论如何,提前感谢您的任何提示和建议。是的,这是家庭作业。不,我没有标记是因为我被告知作业标记不再常用。

Code:

代码:

public class SearchByLyricsWords {
   private static Song[] songs;

   private static TreeMap<String, Song> lyricsTreeMap = new TreeMap<String, Song>();

   private static TreeSet<String> wordsToIgnoreTree = new TreeSet<String>();
   private static File wordsToIgnoreInput = new File("ignore.txt");
   private static String wordsToIgnoreString;
   private static String[] wordsToIgnoreArray;

   private Song[] searchResults;  // holds the results of the search
   private ArrayList<Song> searchList = new ArrayList<Song>();  

public SearchByLyricsWords(SongCollection sc) throws FileNotFoundException {

  // Create a string out of the ignore.txt file
  Scanner scanInputFile = new Scanner(wordsToIgnoreInput);
  String ignoreToken = scanInputFile.next();
  ignoreToken.toLowerCase();
  wordsToIgnoreString = ignoreToken + " ";

  while (scanInputFile.hasNext()) {
     ignoreToken = scanInputFile.next();
     wordsToIgnoreString = wordsToIgnoreString + ignoreToken + " ";
  }

  // Split the string created from ignore.txt 
  wordsToIgnoreArray = wordsToIgnoreString.split("[^a-zA-Z]+");

  // Fill a TreeSet from the wordsToIgnoreArray
  for (int i = 0; i < wordsToIgnoreArray.length; i++) {
     ignoreToken = wordsToIgnoreArray[i];
     wordsToIgnoreTree.add(ignoreToken);
  }

  // Fill TreeMap with lyrics words as the key, Song objects as the value
  songs = sc.getAllSongs();

  for (int j = 0; j < songs.length; j++) {
     Song currentSong = songs[j];
     String lyrics = currentSong.getLyrics();         
     TreeSet<String> lyricsFound = new TreeSet<String>();

     String lyricsToken;
     String[] songLyricsArray;
     songLyricsArray = lyrics.split("[^a-zA-Z]+");

     for (int k = 0; k < songLyricsArray.length; k++) {
        lyricsToken = songLyricsArray[k];

        if (lyricsToken.length() <= 1) {
           continue;
        }

        lyricsFound.add(lyricsToken);
     }

     lyricsFound.removeAll(wordsToIgnoreTree);

     Iterator<String> iterator = lyricsFound.iterator();

     while(iterator.hasNext()) {
        String currentWord = (String)iterator.next();
        lyricsTreeMap.put(currentWord, currentSong);
     }

     //System.out.println(lyricsTreeMap); // testing only
  }
}


public Song[] search(String lyricsWords) {

  lyricsWords = lyricsWords.toLowerCase();
  TreeSet<String> searchTree = new TreeSet<String>();
  String searchToken;
  String[] lyricsWordsSearch = lyricsWords.split("[^a-zA-Z]+");

  for (int l = 0; l < lyricsWordsSearch.length; l++) {
     searchToken = lyricsWordsSearch[l];

     if (searchToken.length() <= 1) {
        continue;            
     }
     searchTree.add(searchToken);
  }
  searchTree.removeAll(wordsToIgnoreTree);

  Iterator<String> searchIterator = searchTree.iterator();

  while(searchIterator.hasNext()) {
     String currentSearchWord = (String)searchIterator.next();
     Collection<Song> lyricsTreeCollection = lyricsTreeMap.values();

     while (lyricsTreeMap.containsKey(currentSearchWord) == true) {

        Iterator collectionIterator = lyricsTreeCollection.iterator();

        while(collectionIterator.hasNext() && collectionIterator.next() == currentSearchWord) {

           Song searchSong = lyricsTreeMap.get(currentSearchWord);
           searchList.add(searchSong);          
        }
     }           
  }
  searchResults = searchList.toArray(new Song[searchList.size()]);

  Arrays.sort(searchResults);

  return searchResults;
}

回答by Louis Wasserman

TreeMaponly keeps one value per key, as with all Mapimplementations:

TreeMap与所有Map实现一样,每个键只保留一个值:

A map cannot contain duplicate keys; each key can map to at most one value.

地图不能包含重复的键;每个键最多可以映射到一个值。

Your alternatives include

您的选择包括

  • use a TreeMap<String, List<Song>>instead, and manually deal with the Listvalues and keeping them updated
  • use e.g. a TreeMultimapfrom Guava, which (more or less) operates like a TreeMap<K, TreeSet<V>>, except a lot better. (Disclosure: I contribute to Guava.)
  • 使用 aTreeMap<String, List<Song>>代替,并手动处理List值并保持更新
  • 使用例如TreeMultimap来自 Guava 的 a,它(或多或少)像 a 一样运行TreeMap<K, TreeSet<V>>,但要好得多。(披露:我为番石榴做出了贡献。)

回答by Bhavik Ambani

I think you shoule set your structure as

我认为你应该将你的结构设置为

Map<String, Set<Song>>

Here Setis used as inner Collection class rather then List. Because it will automatically omit the redundent value for your scenario.

这里Set用作内部 Collection 类而不是List. 因为它会自动省略您的场景的冗余值。

回答by Maria

You need to use this format to initialize the map and set:

您需要使用这种格式来初始化地图并设置:

Map<String, TreeSet<Song>>=new Map<String,treeSet<Song>>();

Than you will need a multiple for loops to loop first thru the song set and than get the Map keys to insert into your Map. Like so:

比您需要多个 for 循环来首先通过歌曲集循环,然后将 Map 键插入到您的 Map 中。像这样:

    for(Song temp:songs){
       for(String word:temp.getLyrics().toLowerCase().split("[^a-zA-Z]+"){
             if(lyricsTreeMap.containsKey(word)){
                  lyricsTreeMap.get(word).add(temp);
    }