我有一个这样的ENUM,我总是得到localFruit
APPLE
或ORANGE
或BANANA
。
public enum Fruits {
// it can have more elements here
APPLE, ORANGE, BANANA;
// some code
}
所以,假设APPLE是我的localFruit
,那么ORANGE
和BANANA
将是我的remoteFruits
。我需要随机播放remoteFruits
,然后确保我的localFruit
位于我的列表顶部,然后是remoteFruits
。
以下是我正在进行洗牌并添加原始result
列表的代码:在下面的代码CURRENT_FRUIT
中可以是APPLE
或ORANGE
或{{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
(但我需要在添加到结果列表之前对其进行随机播放)。
答案 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 很小,上面的内容实际上可能比你的速度慢。
基本上,以上是一种避免在这种情况下复制长列表的方法。有了大名单,这很有帮助。小的,不是那么多。