【问题标题】:Optimizing sequential search in loop优化循环中的顺序搜索
【发布时间】:2018-06-07 07:01:08
【问题描述】:

我正在尝试获取歌曲列表中艺术家的 ArrayList(或 Set,或任何类似内容)。每首歌曲都有 getArtists 函数,它返回参与歌曲的每位艺术家的数组。
目标是有一个艺术家列表,每个艺术家都应该有一个列表(或集合,以更快者为准),其中包含他参与的所有歌曲。

我的代码有效,但速度很慢(1600 首歌曲需要 5 秒)。如何加快速度?

我的代码

private ArrayList<Artist> getArtistsFromSongs(List<Song> songs)
{
    long start = System.currentTimeMillis();

    ArrayList<Artist> artists = new ArrayList<>();
    for (Song song : songs)
    {
        String[] artistsStringArray = song.getArtists();
        for (String artistString : artistsStringArray)
        {
            boolean artistAlreadyExists = false;
            int heExistsAt = -1;

            for (int i = 0; i < artists.size(); i++)
            {
                if (artists.get(i).name.equals(artistString))
                {
                    artistAlreadyExists = true;
                    heExistsAt = i;
                }
            }
            if (artistAlreadyExists)
            {
                artists.get(heExistsAt).songs.add(song);
            } else
            {
                Artist newArtist = new Artist(artistString, new ArrayList<>());
                newArtist.songs.add(song);
                artists.add(newArtist);
            }
        }
    }
    long test = System.currentTimeMillis() - start; //~5500 milliseconds
    return artists;
}

班级

class Artist
{
    public final String name;
    public final ArrayList<Song> songs;

    Artist(String name, ArrayList<Song> songs)
    {
        this.name = name;
        this.songs = songs;
    }
}

提前致谢。

【问题讨论】:

  • 性能优化必须始终附带详细分析。所以,测量它!另外,提供一个完整的例子。我不认为你的迭代本身很慢。所以代码里面可能有一些东西是slow。我们无法仅通过查看代码 sn-p 来判断。获取一个框架并分析您在哪个代码部分花费的时间最多。
  • 请注意,对于数组而言,常规 for 循环比增强型 for 循环稍快。生成的字节码虽然非常相似,但差异非常小。
  • @Zabuza 这个迭代是“缓慢”的根源。我不知道如何为您提供更多详细信息.. 其他所有内容都经过测试并在 1 秒内运行,而这个特定部分大约需要 5.5 秒。查看更新的问题。
  • 是对象歌曲和艺术家的简单对象,还是类似于 jpa/hibernate 实体

标签: java list optimization arraylist set


【解决方案1】:

要提高性能,请将 artistsArrayList&lt;Artist&gt; 更改为 HashMap&lt;String, Artist&gt;

这样您就可以用快速简单的地图查找替换最里面的顺序搜索循环。

Map<String, Artist> artists = new HashMap<>();
for (Song song : songs) {
    for (String artistName : song.getArtists()) {
        Artist artist = artists.get(artistName);
        if (artist == null) {
            artist = new Artist(artistName, new ArrayList<>());
            artists.put(artistName, artist);
        }
        artist.Songs.add(song);
    }
}

在 Java 8+ 中可以改进,特别是如果您稍微更改 Artist 类。

Map<String, Artist> artists = new HashMap<>();
for (Song song : songs) {
    for (String artistName : song.getArtists()) {
        artists.computeIfAbsent(artistName, Artist::new).addSong(song);
    }
}
class Artist {
    public final String name;
    public final ArrayList<Song> songs = new ArrayList<>();

    Artist(String name) {
        this.name = name;
    }

    void addSong(Song song) {
        this.songs.add(song);
    }
}

注意:Java 命名约定是字段名称以小写字母开头,因此更改了上面的第二个解决方案以反映这一点。

【讨论】:

  • 更简洁的代码和更快的速度,但我仍然不明白什么需要 5.5 秒。歌曲列表有多大?数百万,数十亿
  • @gagansingh 我也不明白什么花了这么长时间,但我知道这段代码在 50 毫秒以下完成了完全相同的事情,同时看起来也更干净。顺便说一句:该列表包含大约 1600 首歌曲。
  • 很明显,在修改后的代码中,您正在搜索已编入索引的地图,而在另一种情况下,您每次都在搜索完整列表,因此成本要高一个数量级。我没想到会这么贵,但你总是能学到新东西:)
  • 我对编程比较陌生,之前甚至从未使用过地图。是的,我也从这个答案中学到了很多:)
猜你喜欢
  • 2011-11-30
  • 2011-03-26
  • 2021-10-21
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
  • 1970-01-01
相关资源
最近更新 更多