【问题标题】:Performance vs memory Lists性能与内存列表
【发布时间】:2015-03-13 17:30:37
【问题描述】:

什么会更好?

假设我们有一个特定的类mainClass,它有一个List<BigClass>List<Foo>foos....

Foo 本身有一个List<Boo>

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

或者每次用户请求这个列表时,从“BigClass”中生成这个总计Boo 列表会更好吗?

这里的主要问题是,您是否必须在这里选择性能与内存?

PS。 抱歉,标题太宽泛了,不知道如何命名这个问题:/

【问题讨论】:

  • 使用最容易理解的方法。这是首要问题。在出现问题之前永远不要担心性能或内存,因为它可能不会。
  • 您是否必须在这里选择性能与内存:是的,很明显。您还必须考虑正确性、可维护性、健壮性和可读性。第二种解决方案比第一种解决方案要简单得多。
  • @JBNizet 是的,但如果做得对。会首选第一个选项吗?
  • @MikeDunlavey,这种方法不是很“慢工作”、“冗余工作”吗?如果您必须搜索那些每次碰壁时都可以释放更多内存或性能的函数?
  • @Koen:我已经编程了半个多世纪——我知道什么?先把它弄对。然后,如果有问题,请解决问题。如果你想知道我是如何发现性能问题的,有一个视频here,还有很多在线讨论。

标签: java performance list memory


【解决方案1】:

您可以通过一个实现同时拥有这两种行为。只需在 mainClass 中创建您自己的 List 实现(可能是一个名为 FooList 的内部类,或者甚至可能是一个匿名内部类)。使 FooList 的方法透明地将所有单独的 Foo 列表显示为单个列表。例如,FooList.iterator() 的实现将透明地依次在每个单独的 Foo 列表上实例化一个迭代器,因此它看起来像是在遍历单个大列表。

【讨论】:

    【解决方案2】:

    如果您需要获取所有 Boos 并保持 Boos 分组(即属于某些 FooBoos),那么我会说最好的方法是返回一个查看BigClass中包含的所有Boos,无论它们属于哪个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]
        }
    }
    

    这种方法的最佳之处在于 Guava 返回的 Iterablelazy,这意味着不会创建新的集合或列表并填充任何元素。相反,返回的Iterable 是一个视图,其Iterator 消耗第一个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]
        }
    }
    

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

    【讨论】:

    • 这看起来很不错,因为(据我所知)它不会消耗任何额外的内存。但是,当调用以获取所有 booS 时,它必须一遍又一遍地迭代?
    • @Koen 不,这是一个视图。换句话说,每个Foo 内的所有Boo 列表的包装器。这是一个O(1) 操作。
    • @Koen 非常清楚:当迭代返回的List 时,元素最终被遍历。
    • 所以基本上你可以看到好像Foo 中的所有Boo 列表都是相互关联的?每当我们调用方法 getBoos() 时,都会(一次又一次)创建它?
    • 非常好。只是很奇怪,我从来没有听说过这个......谢谢
    猜你喜欢
    • 1970-01-01
    • 2010-12-26
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 2022-11-10
    • 1970-01-01
    相关资源
    最近更新 更多