有效地将元素添加到列表顶部

时间:2015-06-04 00:33:24

标签: java list arraylist enums linked-list

我有一个这样的ENUM,我总是得到localFruit APPLEORANGEBANANA

public enum Fruits {
    // it can have more elements here
    APPLE, ORANGE, BANANA;

    // some code
}

所以,假设APPLE是我的localFruit,那么ORANGEBANANA将是我的remoteFruits。我需要随机播放remoteFruits,然后确保我的localFruit位于我的列表顶部,然后是remoteFruits

以下是我正在进行洗牌并添加原始result列表的代码:在下面的代码CURRENT_FRUIT中可以是APPLEORANGE或{{1 }}

BANANA

由于这段代码会被调用很多次,所以想看看我做的是否有什么问题,这可能是性能方面的瓶颈?我的代码在性能方面是否合适?

我的主要目标是让private static List<Fruits> getFruitsInOrder() { EnumSet<Fruits> localFruit = EnumSet.of(CURRENT_FRUIT); EnumSet<Fruits> remoteFruits = EnumSet.complementOf(localFruit); List<Fruits> result = new ArrayList<Fruits>(remoteFruits); Collections.shuffle(result); // first element in the list will always be the local fruit result.addAll(0, new ArrayList<Fruits>(localFruit)); return result; } 位于列表的顶部,然后是localFruit(但我需要在添加到结果列表之前对其进行随机播放)。

3 个答案:

答案 0 :(得分:2)

所有这些解决方案都太难了。 Java集非常有效,但简单的数组访问更是如此。另外,构建两个集合(一个具有补充操作),复制到ArrayList,然后使用addAll创建一个新的ArrayList是很多无用的工作和内存垃圾。

enum按顺序为您提供其值的数组。用它!只需将本地元素交换到零位置,然后将数组的其余部分混洗。这样您就可以创建一个数据结构:要返回的ArrayList。其余的只是重新排序其元素。

当然,除非你有一个巨大的枚举或称这个功能数百万次,这个讨论是学术性的。你不会注意到性能差异。

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

enum Fruit { APPLE, PEAR, PEACH, PLUM, BANANA }

public class Hack {

    static List<Fruit> getFruitInOrder(Fruit local) {
        List<Fruit> list = Arrays.asList(Fruit.values());
        Collections.swap(list, 0, local.ordinal());
        Collections.shuffle(list.subList(1, list.size()));
        return list;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(getFruitInOrder(Fruit.PLUM));
        }
    }
}

在我的MacBook上:

run:
[PLUM, BANANA, PEAR, APPLE, PEACH]
[PLUM, PEACH, PEAR, APPLE, BANANA]
[PLUM, PEACH, BANANA, PEAR, APPLE]
[PLUM, PEAR, BANANA, APPLE, PEACH]
[PLUM, PEAR, APPLE, BANANA, PEACH]
[PLUM, BANANA, APPLE, PEACH, PEAR]
[PLUM, APPLE, BANANA, PEACH, PEAR]
[PLUM, APPLE, PEACH, PEAR, BANANA]
[PLUM, APPLE, PEAR, PEACH, BANANA]
[PLUM, PEACH, APPLE, PEAR, BANANA]
BUILD SUCCESSFUL (total time: 0 seconds)

答案 1 :(得分:0)

这个怎么样?它通过第一次正确调整大小来避免任何ArrayList调整大小的性能命中,并且它通过插入列表来避免你所做的命中。它还可以干净地支持多个localFruit。我认为你的版本已经是O(n),所以它不是一个大问题。

private static List<Fruits> getFruitsInOrder() {
    EnumSet<Fruits> localFruit = EnumSet.of(CURRENT_FRUIT);
    EnumSet<Fruits> remoteFruits = EnumSet.complementOf(localFruit);

    List<Fruits> result = new ArrayList<Fruits>(localFruit.size() + remoteFruits.size());
    result.addAll(localFruit);
    result.addAll(remoteFruit);
    Collections.shuffle(result.subList(localFruit.size(), result.size()));
    return result;
}

答案 2 :(得分:0)

假设设置的大小相当小,比如3或4个值,这可能很好。与优化一样,最好对代码进行分析,然后进行优化。话虽如此,代码看起来相当合理,但是您正在制作大量可以避免的列表副本。这是您的代码,每次复制时都会显示注释:

private static List<Fruits> getFruitsInOrder() {
    EnumSet<Fruits> localFruit = EnumSet.of(CURRENT_FRUIT);
    // Creates a new set copying all the fruits from the enum
    EnumSet<Fruits> remoteFruits = EnumSet.complementOf(localFruit);

    // Copies the set above
    List<Fruits> result = new ArrayList<Fruits>(remoteFruits);
    Collections.shuffle(result);

    // Will allocate a new array, copy the remote fruits, then add
    // another copy of the local fruit
    result.addAll(0, new ArrayList<Fruits>(localFruit));
    return result;
}

这些副本都是 O(n),其中 n 是水果的数量。如果 n 小于10左右(甚至可能小于100左右),这并不是那么糟糕。还有分配和释放所有这些对象的成本。再一次,并不可怕但也不伟大。这是一个只有一个副本的替代实现:

private static List<Fruits> getFruitsInOrder() {
    // Makes one copy of all the fruits, that's it.
    ArrayList<Fruits> allFruits = new ArrayList<>(Fruits.values());
    // move the current fruit to the front. Assumes that the value of the
    // fruit is its array index: if not, you'll have to maintain that 
    // explicitly in a Map<Fruits, int>
    int curFruitIdx = CURRENT_FRUIT.value();
    Fruits curFruit = allFruits.get(curFruitIdx);
    allFruits.set(curFruitIdx, allFruits.get(0));
    allFruits.set(0, curFruit);
    // Now get a **view**, not a copy, of the rest of the list and shuffle it
    List<Fruits> toShuffle = allFruits.subList(1, allFruits.size());
    Collections.suffle(toShuffle);
    return allFruits;
}

如果事情不需要是线程安全的,那么即使是方法顶部的单个副本也可以被删除:你可以只在一个静态的地方粘贴一个副本并继续操纵它。同样,如果 n 很小,上面的内容实际上可能比你的速度慢。

基本上,以上是一种避免在这种情况下复制长列表的方法。有了大名单,这很有帮助。小的,不是那么多。