我想创建可能包含重复值的集合,没有特定的顺序。
换句话说:
{ 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 }
事实上,我希望拥有一组这些集合,因此如果我尝试添加{ 1, 1, 2 }
和{ 2, 1, 1 }
,则第二个.add()
实际上不会执行任何操作。
是否有标准集合已经表现出来?
如果我理解正确:
我是否忽略了一个允许重复值和任意或固定顺序的集合,以便考虑两个具有相同元素的集合?
@asteri询问我的用例。在游戏中,我有不同长度的块,可以端到端铺设以填充一定距离。例如,如果距离是10,则可以用2-3-5或5-2-3或3-3-4或3-4-3或任何数量的其他排列填充。根据可用的块,我想列出所有可能解决的集合,填补空白。
CUSTOM SOLUTION
@sprinter建议创建ArrayList的子类。 @dasblinkenlight和@Dici建议使用Map存储{ Element : Count }
个条目。我选择将这两个建议结合起来。下面是TreeMap的子类。密钥始终以相同的顺序存储,以确保hashCode()方法生成相同的值,例如使用相同的键和值。
我使用了increment
方法,可以轻松添加特定整数"值"的新出现次数。
package com.example.treematch;
import java.util.Map;
import java.util.TreeMap;
public class TreeMatch<K> extends TreeMap<K, Integer> {
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof TreeMatch)) {
return false;
}
TreeMatch otherMatch = (TreeMatch) other;
if (size() != otherMatch.size()) {
return false;
}
for (Object key : this.keySet()) {
if (!otherMatch.containsKey(key)) {
return false;
}
}
for (Object key : otherMatch.keySet()) {
if (!this.containsKey(key)) {
return false;
}
if (this.get(key) != otherMatch.get(key)) {
return false;
}
}
return true;
}
public void increment(K key) {
Integer value;
if (this.containsKey(key)) {
value = (this.get(key)) + 1;
} else {
value = 1;
}
this.put(key, value);
}
@Override
public int hashCode() {
int hashCode = 0;
for (Map.Entry entry : this.entrySet()) {
hashCode += entry.getKey().hashCode();
hashCode = hashCode << 1;
hashCode += entry.getValue().hashCode();
hashCode = hashCode << 1;
}
return hashCode;
}
}
答案 0 :(得分:6)
Java内置库中没有任何内容,但是Guava的Multiset
可以做到这一点。
答案 1 :(得分:3)
此类收集通常称为 multiset 。标准库中没有包含多个集合的实现,但外部库Guava确实包含多个集合。
答案 2 :(得分:3)
您可以使用Bag
中的Eclipse Collections,也称为Multiset
。
MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2);
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1);
Assert.assertEquals(bag1, bag2);
由于您的收藏只包含整数,因此最好使用原始包。 IntHashBag
由int[]
数组支持,而不是Object[]
数组,使用更少的内存,可能更快。
MutableIntBag intBag1 = IntBags.mutable.with(1, 1, 2);
MutableIntBag intBag2 = IntBags.mutable.with(1, 2, 1);
Assert.assertEquals(intBag1, intBag2);
注意:我是Eclipse集合的提交者。
答案 3 :(得分:2)
我认为更好的选择是以这种方式包裹Map
:
Map.keySet()
相等的相等方法,如果出现次数很重要,Map.equals()
否则答案 4 :(得分:2)
HashMap<Integer,Integer>
将能够表示您的集合。例如,您的集合{1, 1, 2}
将表示如下:
{{ 1 : 2 }, { 2 : 1 }}
意味着有两个项目的值为1
(第一对整数),一个项目的值为2
(第二对整数)。
答案 5 :(得分:1)
我建议创建一个覆盖equals方法的ArrayList子类:
class MultiMemberList extends ArrayList {
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof MultiMemberList))
return false;
MultiMemberList otherList = (MultiMemberList)other;
if (size() != otherList.size())
return false;
return stream().distinct().allMatch(element -> countElement(element) == otherList.countElement(element));
}
private int countElement(Object element) {
return stream().filter(element::equals).count();
}
}
我没有把它变得简单,但是显然你应该这样做。
您还应该覆盖哈希函数:最简单的方法是在调用超类的哈希值之前对列表进行排序。
一旦你实现了这个,那么添加了MultiMemberList对象的Set将按预期工作:根据这个定义,它不会添加第二个等于第一个的列表。