首页 > 解决方案 > 如何从由自定义对象组成的 ArrayLists 的 ArrayLlist 中删除重复项

问题描述

我有一个递归函数,它生成一个列表,用于跟踪纸牌游戏的有效手牌组合:

List<List<HandComponent>> validSCompArrangements = new ArrayList<>();

该列表由递归函数成功填充,但通常具有重复的子列表(按内容但不是按顺序),由于函数的必需性质,这些子列表是不可避免的。我希望删除这些重复的子列表条目 (List<\HandComponent>),以便最终上述列表仅包含内容唯一的子列表,因为顺序无关紧要。

这是 HandComponent 类的重要部分:

public class HandComponent {

    private Type mType;
    private Card mCard; // For runs this is the middle card
    private Source mSource;

    public HandComponent(Type type, Card card, Source source)
    {
        init(type, card, source);
    }

    public enum Type {PAIR, TRIPLE, QUAD, RUN}
    public enum Source {STOLEN, SECRET, EITHER}
...
}

如果子列表 List 包含完全相同的 HandComponents(即每个列表的组件之间的 Type、Card 和 Source 必须相同),则应该认为它等于另一个子列表。Card 是另一个文件中定义的另一个枚举。

因此,如果“validSCompArrangements”中的两个列表是

(PAIR,CARD1,STOLEN), (TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET)

(TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET), (PAIR,CARD1, STOLEN)

它们应该被认为是相同的,因为它们最终包含相同的 HandComponents,即使顺序不同并且应该删除一个,以便“validSCompArrangements”只包含该唯一列表一次。

调查这个我发现了关于如何解决这个问题的点点滴滴,但没有一个具有列表列表与自定义对象组合的功能。一种方法似乎是实现一个自定义比较器,该比较器将 HandComponent 实例与 Collections 进行比较,以便对子列表进行排序,然后另一个自定义比较器来比较这些排序的子列表是否存在重复项,尽管这看起来有点笨拙,而且我我不完全确定如何覆盖比较方法以及我需要为每个比较器期望的返回类型。我看到的唯一另一件事是,因为对于我的使用,子列表和主“validSCompArrangements”列表本身的顺序并不重要,所以我应该使用 Sets 和 HashSet 来解决这个问题反而,

总的来说,我有点困惑,因为我可以设法找到与此远程相关的任何示例通常只讨论一个包含基元而不是枚举的自定义对象列表,或者仅使用基元而不使用自定义的列表列表对象。这是一个自定义对象列表的列表,其成员是枚举,这一事实让我对如何去做这件事有点迷茫。

例如,这个问题中的标记答案:Using collection to remove duplicate Lists,它只处理我的一部分问题,尽管 OP 说它似乎对我不起作用。按原样运行该代码,而不是更改

Set<Integer> dedupedCollection = new HashSet<Integer>();

Set<List<Integer>> dedupedCollection = new HashSet<>();

显然,它会生成 3 个条目的集合,其中 5、10、5 的第二个条目不会被视为重复项,并且不会像 OP 建议的那样被忽略。

编辑:

到目前为止,我发现的最接近的事情是将我的顶级列表转换为 HashSet 使用:

Set<List<HandComponent>> handSet = new HashSet<>(validSCompArrangments);

但这只会消除重复列表,如果它们的顺序相同(我猜这是由于 List 的默认实现“equals()”的性质),而我需要它来考虑内容相同但不同的列表订单也重复。解决此问题的一种方法是将 Sets 也用于 HandComponent 子列表,因为它们天生不关心顺序,但这将防止这些集合具有重复的 HandComponents,而我确实需要被允许。

标签: javaarraylistduplicateshashsetsublist

解决方案


正如你所说,你只需要实现equals:)

我已经为您提供了如何在类中实现 equals 方法HandComponent以及如何使用 HashSet 只获取没有重复的组合。

我已经在 J​​ava 8 中实现了它,如果你愿意,你也可以尝试使用 for 循环来改变它:)

这是equals`HandComponent的实现

public class HandComponent {

public enum Type {PAIR, TRIPLE, QUAD, RUN}

public enum Source {STOLEN, SECRET, EITHER}

public enum Card {ACE, ONE, TWO, TRHEE}

private Type type;
private Card card;
private Source source;

public HandComponent(Type type, Card card, Source source) {
    this.type = type;
    this.card = card;
    this.source = source;
}

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (!(o instanceof HandComponent)) {
        return false;
    }
    HandComponent handComponent = (HandComponent) o;

    if (type != handComponent.type) {
        return false;
    }

    if (card != handComponent.card) {
        return false;
    }

    if (source != handComponent.source) {
        return false;
    }

    return true;
}

@Override
public String toString() {
    return "HandComponent=[" + String.join(", ", Arrays.asList(type.toString(), card.toString(), source.toString())) + "]";
}
}

在下面您可以看到如何使用它

public class Main {

public static void main(String[] args) {

    // Creating 2 hand components
    HandComponent handComponent1 = new HandComponent(HandComponent.Type.PAIR, HandComponent.Card.ACE, HandComponent.Source.STOLEN);
    HandComponent handComponent2 = new HandComponent(HandComponent.Type.QUAD, HandComponent.Card.TRHEE, HandComponent.Source.EITHER);

    // 2 combinations with the same card, but different order => they are the same
    List<HandComponent> firstCombination = Arrays.asList(handComponent1, handComponent2);
    List<HandComponent> secondCombination = Arrays.asList(handComponent2, handComponent1);

    // Mixing 2 combinations together
    List<List<HandComponent>> combinations = Arrays.asList(firstCombination, secondCombination);

    // printing the mix
    System.out.println("Before: " + combinations);

    // removing duplicates
    List<ArrayList<HandComponent>> collect = combinations.stream() // having a stream of list<HandComponent>
            .map(HashSet::new) // converting to HashSet, which mean there won't be duplicate in the combinations.
            .distinct()        // getting only the distinct combinations
            .map(ArrayList::new) // reconverting to array list
            .collect(Collectors.toList()); // collecting them as list

    // result without duplicates
    System.out.println("After: " + collect);

    // You can now implement it with loop and no java 8 :)
}
}

推荐阅读