从集合中获取随机元素
Posted
技术标签:
【中文标题】从集合中获取随机元素【英文标题】:Getting a random element from a Collection 【发布时间】:2022-01-22 19:42:37 【问题描述】:在 java 中,我希望能够始终维护按物种分类的鱼类集合(因此使用 HashMap),同时能够从所有物种中选择一个随机元素,除了具有恒定时间复杂度的物种。例如,以下代码可以完成这项工作,但复杂度为 O(元素数量):
import java.util.*;
HashMap<String, ArrayList<Fish>> fishesBySpecies = new HashMap<>();
// Insert some fishes...
// Fish has a String attribute that describes its species
// Now we want to pick a random Fish that isn't from the unwantedSpecies
String unwanted = "unwanted species";
ArrayList<Fish> wantedSpecies = new ArrayList<>();
for (String species : fishesBySpecies.keySet())
if (!Objects.equals(species, unwanted))
wantedSpecies.addAll(fishesBySpecies.get(species));
// Finally !
int randomIndex = new Random().nextInt(wantedSpecies.size());
Fish randomElement = wantedSpecies.get(randomIndex);
如果可能的话,知道如何以恒定的时间复杂度来做到这一点吗?谢谢!
【问题讨论】:
@Sweeper 抱歉,我编辑了我的问题,然后忘记了最初的标题,现在已修复。 当然,在恒定的时间复杂度下这是不可能的。但显然,您可以通过使用 Stream API 重写代码以使其看起来更复杂,甚至不提及时间复杂度...... @Holger 最后我找到了一个使用地图的 O(物种数量)的解决方案,但它对我的要求太具体了,我无法唤起它。我相信它不太可能帮助其他人。我接受了第一个答案,因为它帮助我更好地理解我想要做什么。 您的问题确实已经有 O(元素数量),正如您自己所说的那样。因此,如果您不想要更好的时间复杂度,这无论如何都是不可能的,那么就不清楚您要的是什么。选择一个没有中间ArrayList
的随机元素是可能的,如果你愿意,只要问正确的问题。
【参考方案1】:
我能想到的唯一方法是维护ArrayList<Fish>
以及您已有的地图。但是有一个缺点:添加或删除鱼会稍微复杂一些:
Map<String, List<Fish>> fishesBySpecies = new HashMap<>();
List<Fish> wantedFishes = new ArrayList<>();
//...
public void addFish(String species, Fish fish)
List<Fish> speciesFishes = fishesBySpecies.get(species);
if (speciesFishes == null)
speciesFishes = new ArrayList<>();
fishesBySpecies.put(species, speciesFishes);
speciesFishes.add(fish);
// also maintain the list of wanted fishes
if (!unwantedSpecies.equals(species))
wantedFishes.add(fish);
【讨论】:
【参考方案2】:您正在执行的是过滤,过滤时您必须检查每个元素是否需要取出。您可以尝试对键使用字母排序,并在键按字母顺序大于过滤(不需要的)键时停止过滤。
您的代码也可以通过使用 java 流来彻底缩短:
HashMap<String, ArrayList<Fish>> fishesBySpecies = new HashMap<>();
// Insert some fishes...
// Fish has a String attribute that describes its species
// Now we want to pick a random Fish that isn't from the unwantedSpecies
String unwanted = "unwanted species";
fishesBySpecies.keySet().stream() // Get the keyset and create a stream out of it
.filter(key -> !key.equalsIgnoreCase(unwanted)) // If key is not equal to unwanted then leave it in else remove it
.forEach(filteredKey ->
wantedSpecies.addAll(fishesBySpecies.get(filteredKey))); // For each key that was left in, we fetch the fishes
或
fishesBySpecies.keySet().stream() // Get the keyset and create a stream out of it
.forEach(key ->
if(!key.equalsIgnoreCase(unwanted))
wantedSpecies.addAll(fishesBySpecies.get(unwanted));
); // iterate and filter at the same time. Faster.
【讨论】:
以上是关于从集合中获取随机元素的主要内容,如果未能解决你的问题,请参考以下文章