Java 比较相同内容的iterables,但不考虑顺序

Java 比较相同内容的iterables,但不考虑顺序,java,collections,iterator,Java,Collections,Iterator,我试图比较Java中两个大小相同的iTerable。我只需要知道内容是一样的。然而,像[1,2]和[1,2,2]这样的东西不应该相等,而[1,2,2,4]应该等于[1,2,4,2] boolean函数名(){ 布尔pvk; …设置。。。 对于(边e:pMST.edges()){ pvk=假; 对于(边f:kMST.edges()){ 如果(e==f){ pvk=真; System.out.println(“True”); } } 如果(!pvk)返回false; } 返回true; } 这是我

我试图比较Java中两个大小相同的iTerable。我只需要知道内容是一样的。然而,像[1,2]和[1,2,2]这样的东西不应该相等,而[1,2,2,4]应该等于[1,2,4,2]

boolean函数名(){
布尔pvk;
…设置。。。
对于(边e:pMST.edges()){
pvk=假;
对于(边f:kMST.edges()){
如果(e==f){
pvk=真;
System.out.println(“True”);
}
}
如果(!pvk)返回false;
}
返回true;
}

这是我最初糟糕的尝试,但它不仅总是返回false,而且不能正确解释重复项

我会把它们分类。但首先我会在排序之前比较大小。您需要提供排序方法使用的
比较器。如果要对整数进行排序,可以使用:

List a=newarraylist(List.of(1,2,3,3,3,3,4,5,6));
列表b=新的数组列表(列表(2,3,1,3,4,5,6,3,3));
System.out.println(compareList(a,b,Comparator.naturalOrder());
公共静态布尔比较列表(列表1、列表2、,
比较器(comp){
if(list1==list2){
返回true;
}
如果(list1.size()!=list2.size()){
返回false;
}
Collections.sort(列表1,comp);
Collections.sort(列表2,comp);
返回列表1.equals(列表2);
}
您可以对项目进行排序并比较结果列表,但这可能会很慢O(n lg n),它依赖于项目的可比性或比较器对其施加的总顺序。这可能是不可行的

这建议使用番石榴多糖。这是有意义的,因为它跟踪元素和发生次数,这对您的问题很重要。对于合理的实现,例如一个。其他库,如apachecommons()和eclipsecollections()的集合实现在功能上等同于Guava的Multiset

如果您不想在这些库中包含依赖项,可以在JDK中自行实现。不幸的是,Java没有Bag实现,但为此,使用从项目类型到计数(整数或长)的映射很容易模拟它

如果您有列表,可以执行以下操作:

boolean unorderedEquals(List<Item> list1, List<Item> list2) {
    Map<Item, Long> freq1 = list1.stream().collect(groupingBy(i -> i, counting()));
    Map<Item, Long> freq2 = list2.stream().collect(groupingBy(i -> i, counting()));
    return freq1.equals(freq2);
}
结合来自的想法,特别是为了创建一个高效但可读的解决方案,您可以使用

static boolean unorderedEquals(Collection<?> coll1, Collection<?> coll2) {
    if(coll1.size() != coll2.size()) return false;
    Map<Object, Integer> freq = new HashMap<>();
    for(Object o: coll1) freq.merge(o, 1, Integer::sum);
    for(Object o: coll2)
        if(freq.merge(o, -1, Integer::sum) < 0) return false;
    return true;
}
如果我们想要避免装箱开销,我们必须求助于地图的可变值,例如

static boolean unorderedEquals(Collection<?> coll1, Collection<?> coll2) {
    if(coll1.size() != coll2.size()) return false;
    Map<Object, int[]> freq = new HashMap<>();
    for(Object o: coll1) freq.computeIfAbsent(o, x -> new int[1])[0]++;
    int[] absent = { 0 };
    for(Object o: coll2) if(freq.getOrDefault(o, absent)[0]-- == 0) return false;
    return true;
}

当它们的计数达到零时,它将从映射中删除条目,因此我们只需在末尾检查映射是否为空。

Edge是一个对象。使用
equals
而不是
=
是否需要更多的元素?因为关于内存占用,我想不出一个不使用
n
元素临时容器的解决方案(至少在某个点上)。就地解决方案显然是最好的,但额外的内存可能适合我的输入大小。事实上,我找到了答案,对于具有重复项的iterables来说,这只是稍微提高了内存效率。尽管如此,它应该已经对您有所帮助。您可以使用
映射来建模,其中键是数字,值是发生的次数。回答得好,但我不想使用外部库来解决这个问题。@SolidSnackDrive这就是为什么这个答案显示了一个不使用第三方库的解决方案。@Holger你是对的。可悲的是,就在几天前,我使用了这个特性,并思考了必须有位置一对一映射的要求。
static boolean unorderedEquals(Iterable<?> iter1, Iterable<?> iter2) {
    Map<Object, Integer> freq = new HashMap<>();
    int size = 0;
    for(Object o: iter1) {
        freq.merge(o, 1, Integer::sum);
        size++;
    }
    for(Object o: iter2)
        if(--size < 0 || freq.merge(o, -1, Integer::sum) < 0) return false;
    return size == 0;
}
static boolean unorderedEquals(Collection<?> coll1, Collection<?> coll2) {
    if(coll1.size() != coll2.size()) return false;
    Map<Object, int[]> freq = new HashMap<>();
    for(Object o: coll1) freq.computeIfAbsent(o, x -> new int[1])[0]++;
    int[] absent = { 0 };
    for(Object o: coll2) if(freq.getOrDefault(o, absent)[0]-- == 0) return false;
    return true;
}
static boolean unorderedEquals(Iterable<?> coll1, Iterable<?> coll2) {
    Map<Object, int[]> freq = new HashMap<>();
    for(Object o: coll1) freq.computeIfAbsent(o, x -> new int[1])[0]++;
    int[] absent = {};
    for(Object o: coll2)
        if(freq.compute(o, (key,c) -> c == null || c[0] == 0? absent:
                                      --c[0] == 0? null: c) == absent) return false;
    return freq.isEmpty();
}