从方法返回空集合

时间:2014-08-12 08:15:45

标签: java performance collections garbage-collection

在第二种方法中使用第一种方法是否有任何内存/性能优势?

第一

public List<Integer> getList1(List<Integer> data) {
    List<Integer> list = new ArrayList<Integer>();
    for (Integer element: data) {
        if (element % 2 == 0) {
           list.add(element);
        }
    }
    return list.isEmpty() ? Collections.<Integer>emptyList() : list;
}

第二

public List<Integer> getList2(List<Integer> data) {
    List<Integer> list = new ArrayList<Integer>();
    for (Integer element: data) {
        if (element % 2 == 0) {
           list.add(element);
        }
    }
    return list;
}

3 个答案:

答案 0 :(得分:7)

任何重要的程度取决于调用getList1的频率,当然它会返回一个空列表。但是:

Collections#emptyList的JavaDoc提供了见解:

  

实施说明:此方法的实现无需为每次调用创建单独的List对象。使用此方法可能具有与使用同名字段相当的成本。 (与此方法不同,该字段不提供类型安全性。)

所以是的,如果你使用emptyList而不是每次返回一个新的空列表,你(可能)最终会减少内存中的对象,因为Collections#emptyList可以自由地返回 List<Integer>每次调用它(因为它是不可变的)。

可能 也会获得不相关的性能提升(至少使用Oracle的JVM)。在某些情况下,我根本不了解细节,但在某些情况下,Oracle的JVM最初可以在堆栈上分配分配给局部变量的对象,只有在传输它们时才会将它们传输到堆中。对它们的引用在函数调用终止后仍然存在。基于堆栈的分配确实快,当然不会受到碎片的影响。因此,在空列表方案中,您不仅可以避免使用空列表混乱内存,还可以获得速度优势(JVM不必将您在堆栈中创建的空列表复制到堆中)。但同样,我不知道JVM优化是否必然适用于您的代码。

如果内存优化真的对你的代码很重要,你不想依赖堆栈分配的东西,只需要轻微牺牲代码清晰度和 tiny 运行时成本,如果您不需要,可以避免分配列表

public List<Integer> getList1(List<Integer> data) {
    List<Integer> list = null;
    for (Integer element: data) {
        if (element % 2 == 0) {
           if (list == null ) {
             list = new ArrayList<Integer>();
           }
           list.add(element);
        }
    }
    return list == null ? Collections.<Integer>emptyList() : list;
}

但请注意,存在重要的语义差异。在您的第一个代码块中,getList1不一致:它可能返回调用代码可以修改的列表(如果它不为空),或者它可能返回一个不可变列表(如果它是空的)。 getList1应该总是返回一个可变列表或不可变列表,有时候不会返回一个列表,有时会返回另一个列表。如果它可以是不可变的,你应该使用Collections#unmodifiableList来获取非空列表的不可变版本,并返回它。

答案 1 :(得分:0)

既然你要回归list,为什么会这样?这只是多余的。

首选第二种方式。

另外,请参阅emptyList()方法文档

  

实施说明:此方法的实现无需为每次调用创建单独的List对象。使用此方法可能具有与使用同名字段相当的成本。 (与此方法不同,该字段不提供类型安全性。)

答案 2 :(得分:0)

有一个微妙的区别。

如果getList1返回空列表,则该列表不可变

List<Integer> l1 = getList1();
assertTrue(l1.isEmpty());
l1.add(1); //exception

List<Integer> l2 = getList2();
assertTrue(l2.isEmpty());
l2.add(1); //OK

此外:

List<Integer> l1a = getList1();
List<Integer> l1b = getList1();
assertTrue(l1a.isEmpty());
assertTrue(l1b.isEmpty());
assertSame(l1a, l1b); //same reference so there is some saving in memory

List<Integer> l2a = getList2();
List<Integer> l2b = getList2();
assertTrue(l2a.isEmpty());
assertTrue(l2b.isEmpty());
assertNotSame(l2a, l2b); //not same reference, two unique lists

这两者是相互关联的,如果它不是不可变的,那么共享引用将不起作用。