避免在for循环中创建多个类对象 - 性能

时间:2016-01-25 20:30:40

标签: java android performance for-loop garbage-collection

假设我有一个名为歌曲的课程,其中包含许多信息,例如

  • 歌曲标题
  • 歌曲艺术家
  • 歌曲文件扩展名
  • 其他歌曲信息......

此课程中的setter设置歌曲标题。

public class Song {
    private String title;
    private String artist;
    ...

    public Song(String songTitle, String songArtist, ...) {
        this.title=songTitle;
        this.artist=songArtist;
        ...
    }

    public void setTitle(String Songname){
        this.title = Songname;
    }

    public String getTitle(){return title;}
    public String getArtist(){return artist;}
    ...
}

我有一个 Strings 列表,其中包含不同的歌曲标题,但没有关于这些歌曲的其他信息。现在我想填充 Songs 列表,并在setter的帮助下将这些Songs类的标题设置为List Strings 中的标题。我在for语句中执行此操作,但如果我检查List条目,我只会获得添加的最后一首Song的标题。

int index = 0;
Song songClass = new LocalSong(null, null, ...);
for (final String song : Songs) {
    try {
        songClass.setTitle(song);
        //Set Titles for Local Song Objects
        AnotherList.add(index, null);
        AnotherList.set(index, songClass);
        System.out.println("Title of Song number " + index + " is " + AnotherList.get(index).getTitle());
        index++;
    } catch (Exception e) {e.printStackTrace();}
}

我发现当我不使用Song类中的setter并在每个 Strings 列表的for语句中创建一个新的Song对象时,它可以工作。像这样:

int index = 0;
for (final String songname : Songs) {
    try {
        Song songClass = new LocalSong(songname, null, ...);
        //Set Titles for Local Song Objects
        AnotherList.add(index, null);
        AnotherList.set(index, songClass);
        System.out.println("Title of Song number " + index + " is " + AnotherList.get(index).getTitle());
        index++;
    } catch (Exception e) {e.printStackTrace();}
}

但该解决方案性能苛刻?例如,如果我在 Strings 列表中有数百个条目,我会创建数百个我不会使用的歌曲类,或者垃圾收集器是否处理好这个?

此外,是否有更好的方法来创建带有标题的歌曲,我从字符串列表中获取。

5 个答案:

答案 0 :(得分:3)

你指的是Song类的实例,而不是很多类,面向对象的编程所有关于创建实例的任何时候你都有可识别的不同物体。现代Java框架中的单个HTTP请求通常会创建并丢弃数千个对象,并且JVM可以很好地处理它。

更广泛地说,除非您有演示的性能问题,否则不要优化性能 - 它实际上很慢,而调查(例如性能分析)会告诉您问题所在。

答案 1 :(得分:2)

您似乎对创建新对象实例的关键字new有一个基本的误解。如果您只创建了一个对象,那么列表中的所有对象引用都指向同一个对象,因此无论访问哪个引用,它都是最后一个指定值所见的setter。

您也在尝试进行所谓的"过早优化"。请注意这一点,因为您经常过于复杂,因此通过关注这一点会对代码的可维护性产生负面影响。可以轻松地进行简单的优化或选择替代方案,只要它们很容易或很容易重构,但要小心你并没有解决不存在的问题。

答案 2 :(得分:1)

第二个解决方案的工作原因是因为您在for循环的每次迭代中创建一个Song类的新实例并将其添加到列表中。

在第一个解决方案中,您需要在for循环songClass的每次迭代中添加SAME实例。您在此处所做的是更改songClass的标题,并将其添加到列表中。

对于垃圾收集,在代码的这个阶段创建包含大量对象的列表是完全正常的。垃圾收集器将在您不再使用它们时解除分配对象。

答案 3 :(得分:1)

这里没有什么不寻常之处,在第一个示例中,您重用同一个对象,您的列表包含对同一个对象的相同引用。由于您修改了它,因此您也在修改整个列表。

  

但这不是解决方案性能问题吗?

不,至少在你测量它需要太多内存或你的循环执行花费太多时间之前。如果发生这种情况,那么你应该重新设计你的代码,即。处理您的歌曲列表,只有用户在屏幕上看到的内容。也许重新设计你的用户界面,允许你的用户按字母顺序查看歌曲 - 只在后面的A,B,C,... - 任何适合你的应用程序。

答案 4 :(得分:0)

限制多个实例的最简单方法

raise UndefinedValueError('{} not found. Declare it as envvar or define a default value.'.format(option))
decouple.UndefinedValueError: PASSWORD not found. Declare it as envvar or define a default value.