繁体   English   中英

如何优化此代码?

[英]How can I optimize this code?

我当前的项目是使用Java中的TreeSet和TreeMap,并从文本文件中读取10514个Song元素的输入数组。 每首歌曲均包含“艺术家”,“标题”和“歌词”字段。 该项目的目的是使用集合和地图对歌词进行快速搜索。

首先,我对输入的Song数组进行迭代,访问lyrics字段,并创建一个Scanner对象以使用以下代码对歌词进行迭代: commonWords是一个不应该是键的单词的TreeSet,而lyricWords是要映射的单词的整体映射歌曲。

public void buildSongMap() {
    for (Song song:songs) {
        //method variables
        String currentLyrics= song.getLyrics().toLowerCase(); 
        TreeSet<Song> addToSet=null;
        Scanner readIn= new Scanner(currentLyrics);
        String word= readIn.next();

        while (readIn.hasNext()) {

            if (!commonWords.contains(word) && !word.equals("") && word.length()>1) {
                if (lyricWords.containsKey(word)) {
                    addToSet= lyricWords.get(word);
                    addToSet.add(song);
                    word=readIn.next();
                } else 
                    buildSongSet(word);

            } else 
                word= readIn.next();
        }

    }

为了构建songSet,我使用以下代码:

public void buildSongSet(String word) {     
    TreeSet<Song> songSet= new TreeSet<Song>();
    for (Song song:songs) {
        //adds song to set 
        if (song.getLyrics().contains(word)) {
            songSet.add(song);
        }
    }
    lyricWords.put(word, songSet);
    System.out.println("Word added "+word);
}

现在,由于从循环内部调用了buildSongSet,因此创建映射的时间为N ^ 2。 当输入数组为4首歌曲时,搜索运行非常快,但是当使用10514个元素的完整数组时,在具有6 GiB RAM的2.4GHz机器上构建地图可能需要15分钟以上的时间。 我该怎么做才能使此代码更高效? 不幸的是,减少输入数据不是一种选择。

看来您的buildSongSet做多余的工作。 您的封锁:

if (lyricWords.containsKey(word)) {
    addToSet= lyricWords.get(word);
    addToSet.add(song);
    word=readIn.next();
} 

将歌曲添加到现有集合中。 因此,当您找到一个不知道的单词时,只需在其中添加一首歌曲即可。 将buildSongSet更改为:

public void buildSongSet(String word, Song firstSongWithWord) {     
    TreeSet<Song> songSet= new TreeSet<Song>();
    songSet.add(firstSongWithWord);
    lyricWords.put(word, songSet);
    System.out.println("Word added "+word);
}

如果剩下的要迭代的歌曲包含该单词,那么它们将从第一段代码添加到该歌曲集中。 我认为应该可以。

编辑只是看到这是家庭作业...因此删除了HashSet建议。

好吧..所以假设您按歌词顺序排列以下歌曲:

  • 歌曲1-foo
  • 歌曲2-foo bar
  • 歌曲3-Foo Bar Baz

歌曲1将看到foo不包含lyricWords,因此它将调用buildSongSet并为foo创建一个集合。 它将自身添加到包含foo的集合中。

歌曲2将看到foo在lyricWords中,并将其自身添加到集合中。 它会看到bar不在集合中,并创建一个集合并添加自身。 自从第一次看到该单词是在Song 2中以来,就不需要遍历以前的歌曲。

乐曲3遵循相同的逻辑。

您可以尝试优化代码的另一件事是想出一种不处理歌词中重复单词的方法。 如果您的歌词是foo foo foo foo bar bar bar bar foo bar,那么您将进行很多不必要的检查。

编辑还可以看到rsp的答案 -在那里有更多的加速,但是最大的加速却摆脱了内循环-很高兴现在下降到15秒。

imho不需要整个buildSongSet()方法,因为您的主循环已经按词将歌曲添加到集合中。 您唯一缺少的是为一个新单词添加了一个集合,例如:

if (lyricWords.containsKey(word)) {
    addToSet= lyricWords.get(word);
} else {
    addToSet = new TreeSet();
    lyricWords.put(word, addToSet);
}
addToSet.add(song);

您没有解决的一个问题是,歌曲中每次出现单词时,歌曲最终都会多次添加到该设置中。

另一个问题是,在一首歌曲仅包含一个单词的情况下,您根本不会添加它! 最好先检查一下状况:

String word = null;
while (readIn.hasNext()) {
    word = readIn.next();

您的条件是执行过多检查(空字符串的长度<1),并且交换检查也可以加快速度:

if (word.length() > 1 && !commonWords.contains(word)) {

请尝试将TreeSet更改为HashSet。 我看不到您从何处获得TreeSet的好处。

如果您想要一种非常可扩展,简单的方法来解决此问题,而性能只需几个毫秒。 考虑lucene http://lucene.apache.org/

有关如何建立索引和搜索的示例,请参阅此处的答案。如何在Lucene 3.0.2中建立索引和搜索文本文件?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM