2015-03-13 77 views
3

什么会更好?性能与内存列表

说我们会有一定的阶级mainClass具有List<BigClass>List<Foo>foos ....

Foo本身具有List<Boo>

如果目标是能够得到所有Boo的对foos的所有元素。 如果用户要在foos中插入新的Foo,那么保留BigClass中的另一个List<Boo>并插入相应的Boo元素会更好吗?

或者每次用户要求这个列表时,从'BigClass`内生成这个总的Boo列表会更好吗?

这里的主要问题是,你会不会在这里选择性能vs内存?

PS。 对不起广泛的标题,不知道如何命名这个问题:/

+2

使用最容易理解的方法。这是主要关心的问题。性能或内存不要担心,直到它是一个问题,因为它可能不会。 – 2015-03-13 17:36:28

+1

*你会不得不在这里选择性能vs内存*:是的,很明显。您还必须考虑正确性,可维护性,健壮性和可读性。第二种解决方案比第一种解决方案简单得多。 – 2015-03-13 17:37:32

+0

@JBNizet,是的,但如果做得对。第一个选项会是首选吗? – 2015-03-13 17:44:17

回答

1

如果需要都得到所有Boo S和也保持Boo与分组(即Boo s表示属于某个Foo),那么我会说,最好将返回的视图所有Boo包含在BigClass中,不管它们属于哪个Foo

要完成此操作,您可以使用Google Guava IterablesJava 8 Stream.flatMap(),具体取决于您的Java版本。

与谷歌番石榴:

class BigClass { 

    List<Foo> foos = new LinkedList<Foo>(); 

    public Iterable<Boo> allBoos() { 
     return Iterables.concat(this.foos); 
    } 
} 

class Boo { 
    final int a; 

    Boo(int a) { 
     this.a = a; 
    } 

    @Override 
    public String toString() { 
     return String.valueOf(this.a); 
    } 
} 

class Foo 
    implements Iterable<Boo> { 

    List<Boo> boos = new LinkedList<Boo>(); 

    @Override 
    public Iterator<Boo> iterator() { 
     return this.boos.iterator(); 
    } 
} 

public class Sample { 
    public static void main(String[] args) { 

     Boo b1 = new Boo(1); 
     Boo b3 = new Boo(3); 
     Boo b5 = new Boo(5); 

     Boo b2 = new Boo(2); 
     Boo b4 = new Boo(4); 
     Boo b6 = new Boo(6); 

     Foo odd = new Foo(); 
     odd.boos.addAll(Arrays.asList(b1, b3, b5)); 

     Foo even = new Foo(); 
     even.boos.addAll(Arrays.asList(b2, b4, b6)); 

     BigClass b = new BigClass(); 
     b.foos.add(odd); 
     b.foos.add(even); 

     System.out.println(b.allBoos()); // [1, 3, 5, 2, 4, 6] 
    } 
} 

最好的这种做法的是,该番石榴返回Iterable,这意味着没有新的集合或列表中创建并填充任何元素。相反,返回的Iterable是一个视图,其消耗第一个Iterable中的元素,并在用尽时“跳转”到下一个Iterable并消耗其元素,并跳转到下一个元素,依此类推,直到最后一个元素最后的Iterable被消耗。

与Java 8:

class BigClass { 

    List<Foo> foos = new LinkedList<Foo>(); 

    public Iterable<Boo> allBoos() { 
     Stream<Boo> s = this.foos.stream().flatMap(
      f -> f.getBoos().stream()); 
     return s::iterator; 
    } 
} 

class Boo { 
    final int a; 

    Boo(int a) { 
     this.a = a; 
    } 

    @Override 
    public String toString() { 
     return String.valueOf(this.a); 
    } 
} 

class Foo { 

    List<Boo> boos = new LinkedList<Boo>(); 

    public List<Boo> getBoos() { 
     return this.boos; 
    } 
} 

public class Sample { 
    public static void main(String[] args) { 

     Boo b1 = new Boo(1); 
     Boo b3 = new Boo(3); 
     Boo b5 = new Boo(5); 

     Boo b2 = new Boo(2); 
     Boo b4 = new Boo(4); 
     Boo b6 = new Boo(6); 

     Foo odd = new Foo(); 
     odd.boos.addAll(Arrays.asList(b1, b3, b5)); 

     Foo even = new Foo(); 
     even.boos.addAll(Arrays.asList(b2, b4, b6)); 

     BigClass b = new BigClass(); 
     b.foos.add(odd); 
     b.foos.add(even); 

     List<Boo> list = new ArrayList<>(); 
     b.allBoos().forEach(boo -> list.add(boo)); 

     System.out.println(list); // [1, 3, 5, 2, 4, 6] 
    } 
} 

关于懒惰同样的考虑也适用于此。

+0

这看起来很不错,因为(据我所知)它不会消耗任何额外的记忆。但是,当一个呼叫让所有'boo's被做出正确的时候,它将不得不一遍遍地重复每一次。 – 2015-03-13 22:52:15

+0

@Koen不,这是一个观点。换句话说,在每个“Foo”中的所有“Boo”列表中都有一个包装。这是一个'O(1)'操作。 – 2015-03-13 22:57:34

+0

@Koen水晶般清澈:元素在迭代返回的List时被最终遍历。 – 2015-03-13 23:03:18

2

你可以有一个单一的实施两种行为。只需在mainClass中创建自己的List实现(可能是一个名为FooList的内部类,或者甚至可能是一个匿名的内部类)。使FooList的方法将所有单独的Foo列表透明地显示为一个列表。例如,FooList.iterator()的实现将依次透明地在每个单独的Foo列表上实例化一个迭代器,以便它似乎遍历一个大的列表。