什么Java集合认为排列是相等的?

什么Java集合认为排列是相等的?,java,collections,Java,Collections,我想创建可能包含重复值的集合,没有特定顺序 换言之: { 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 } 事实上,我想要一组这样的集合,所以如果我尝试同时添加{1,1,2}和{2,1,1},第二个.add()实际上不会做任何事情 是否有一个标准集合已经以这种方式运行 如果我理解正确: ArrayList允许重复值,但顺序固定 HashSet允许顺序是任意的,但不允许重复值 TreeSet确保顺序不变,但不允许重复值 是否有一个我忽略的集合,既允许重复的值,又

我想创建可能包含重复值的集合,没有特定顺序

换言之:

{ 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 }
事实上,我想要一组这样的集合,所以如果我尝试同时添加
{1,1,2}
{2,1,1}
,第二个
.add()
实际上不会做任何事情

是否有一个标准集合已经以这种方式运行

如果我理解正确:

  • ArrayList允许重复值,但顺序固定
  • HashSet允许顺序是任意的,但不允许重复值
  • TreeSet确保顺序不变,但不允许重复值

是否有一个我忽略的集合,既允许重复的值,又允许任意的或固定的顺序,因此两个具有相同元素的集合被认为是相等的?


@asteri询问了我的用例。在游戏中,我有不同长度的方块,可以端对端地放置,以填充一定的距离。例如,如果距离为10,则可以使用2-3-5或5-2-3或3-3-4或3-4-3或任何数量的其他排列填充距离。根据可用的块,我想列出所有可能的集合,以解决填补空白


自定义解决方案
@sprinter建议创建ArrayList的子类@dasblinkenlight和@Dici建议使用映射来存储
{Element:Count}
条目。我选择将这两个建议结合起来。下面是TreeMap的一个子类。键始终以相同的顺序存储,以确保hashCode()方法生成相同的值,例如,使用相同的键和值

我使用了一个
increment
方法,可以很容易地添加一个新的特定整数“值”

package com.example.treematch;
导入java.util.Map;
导入java.util.TreeMap;
公共类树匹配扩展树映射{
@凌驾
公共布尔等于(对象其他){
if(this==其他){
返回true;
}
如果(!(树匹配的其他实例)){
返回false;
}
TreeMatch otherMatch=(TreeMatch)other;
如果(size()!=otherMatch.size()){
返回false;
}
for(对象键:this.keySet()){
如果(!otherMatch.containsKey(键)){
返回false;
}
}
for(对象键:otherMatch.keySet()){
如果(!this.containsKey(键)){
返回false;
}
if(this.get(key)!=otherMatch.get(key)){
返回false;
}
}
返回true;
}
公共无效增量(K键){
整数值;
如果(本文件包含密钥)){
value=(this.get(key))+1;
}否则{
数值=1;
}
这个.put(键、值);
}
@凌驾
公共int hashCode(){
int hashCode=0;
for(Map.Entry:this.entrySet()){
hashCode+=entry.getKey().hashCode();

hashCode=hashCode我认为您更好的选择是以这种方式包装
Map

  • 关键是你的价值观
  • 值是关键点的出现次数
  • 基于
    Map.keySet()
    相等的相等方法如果出现次数重要,则
    Map.equals()
    否则

Java内置库中没有任何内容,但Guava可以做到这一点。

HashMap
可以表示您的集合,如果您将值存储为键,并且该值在映射中显示为值的次数。例如,您的集合
{1,1,2}
将如下表示:

{{ 1 : 2 }, { 2 : 1 }}

这意味着有两个项目的值为
1
(第一对整数),一个项目的值为
2
(第二对整数).

这种类型的集合通常称为多集。

标准库中没有包含多集的实现,但外部库确实包含多集。

我建议创建一个ArrayList的子类,覆盖equals方法:

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();
    }
}
我并没有把它简单化,但显然你应该这样做

您还应该重写hash函数:最简单的方法是在调用超类的hash之前对列表进行排序

一旦您实现了此功能,那么添加了MultiMemberList对象的集合将如您所期望的那样工作:根据此定义,它不会添加与第一个列表相等的第二个列表。

您可以使用from,也称为
Multiset

MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2);
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1);
Assert.assertEquals(bag1, bag2);

注意:我是Eclipse集合的提交者。

嗯……有趣的问题。我想不出这一点,尽管Apache Commons或Guava中可能有帮助。出于好奇,我可以问一下您的用例吗?这不是对您问题的回答,但一个简单的解决方法是将它们作为列表,然后只是sor有趣的是:
->
HashMultiset.create(c1).equals(HashMultiset.create(c2));
@asteri如果您设置了一个类,其
equals()
方法比较排序列表,请确保它也覆盖
hashCode()
它可能也应该使用排序列表来进行计算。@ajb我甚至没有说要将这个过程封装到任何类型的类中。我只是说在某个地方有两个
List
s作为他想要比较的变量。Bam.Apache或Guava总是提供。我想OP更喜欢equals实现,只是ba基于
Map.equals
,而不是
keySet
,因为他们似乎希望每个元素的出现次数很重要?@LouisWasserman实际上,再次阅读这个问题,我认为我是对的,因为
{1,1,2}=={2,1,1}={1,2,1}
与keySet相比,会使{1,1,2}={1,2},我不认为这是对的?@LouisWasserman我把他们两个都包括进来了哈哈,再次感谢你的杰出表现不是你的对手
MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2);
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1);
Assert.assertEquals(bag1, bag2);
MutableIntBag intBag1 = IntBags.mutable.with(1, 1, 2);
MutableIntBag intBag2 = IntBags.mutable.with(1, 2, 1);
Assert.assertEquals(intBag1, intBag2);