如何从由自定义对象组成的ArrayList的ArrayLlist中删除重复项

时间:2018-07-07 00:22:31

标签: java arraylist duplicates hashset sublist

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

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}
...
}

一个子列表列表仅在包含相同的完全相同的HandComponents(即每个列表的组件之间的Type,Card和Source必须相同)时才被视为等于另一个子列表。 Card是另一个文件中定义的另一个枚举。

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

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

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

应将它们视为相同,因为即使顺序不同,它们最终仍包含相同的HandComponent,并且应将其删除,以便“ validSCompArrangements”仅包含该唯一列表一次。

对此,我发现了如何解决此问题的点点滴滴,但没有发现具有自定义对象的列表列表的组合。 一种方法似乎是实现一个自定义的Comparator,该Comparator将HandComponent实例与Collections进行比较以对子列表进行排序,然后实现另一种自定义Comparator来比较这些排序的子列表中的重复项,尽管这看起来有点笨拙,但我我不能完全确定如何重写比较方法,以及我需要为每个比较器提供什么样的返回值。我看到过的唯一一件事是,因为就我的使用而言,子列表和主“ validSCompArrangements”列表本身的顺序都无关紧要,所以我应该使用Sets和HashSet来解决此问题。取而代之的是,我不知道如何使用这些代码来解决此问题,除了我可能需要覆盖我的HandComponent类的hashCode和equals方法,同样,我不确定如何做到这一点。

总的来说,我有点困惑,因为我可以设法找到与之远程相关的任何示例,通常只是谈论一个包含原始语言而不包含枚举的自定义对象列表,或者仅使用原始语言的列表的列表。而且根本没有自定义对象。这是一个自定义对象列表的成员,这些自定义对象的成员都是枚举,这让我迷失了如何解决这个问题。

例如,在该问题中的标记答案: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子列表,因为它们根本不关心顺序,但这可以防止这些Set具有重复的HandComponent,而我确实需要允许它们。

2 个答案:

答案 0 :(得分:1)

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

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

我已经在Java 8中实现了它,如果需要的话,也可以尝试使用for循环对其进行更改:)

这是`HandComponent

equals实现
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 :)
}
}

答案 1 :(得分:0)

对我来说,最有效的方法是按照徐佳杰的建议为我的HandComponent类实现“ equals()”方法,以及通过使用Studio中的选项由Android Studio自动生成的“ hashCode()”方法。上下文菜单或Alt +插入:

 @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    HandComponent that = (HandComponent) o;
    return mType == that.mType &&
            mCard == that.mCard &&
            mSource == that.mSource;
}

@Override
public int hashCode() {

    return Objects.hash(mType, mCard, mSource);
}

然后,我还使该类实现了与Collections类一起使用的Comparable接口,并在“ compareTo()”方法中指定了HandComponent实例的排序顺序优先级,如下所示:

 @Override
public int compareTo(@NonNull HandComponent other) {

    // Check Type first
    int compareResult = mType.compareTo(other.mType);
    if(compareResult == 0)
    {
        // Check Card second
        compareResult = mCard.compareTo(other.mCard);
        if(compareResult == 0)
        {
            // Check Source last
            compareResult = mSource.compareTo(other.mSource);
        }
    }

    return compareResult;
}

由于Comparable for List的默认实现要求列表顺序相同,以便在比较两个列表时返回“ true”,因此我每次想删除重复项时都需要对列表列表进行排序,这非常好后来我从该组织中受益。

最终,通过首先确保对HandComponent的子列表进行了排序,然后创建了顶级列表的HashSet,这使我可以从我的自定义对象列表中删除重复项。

List<List<HandComponent>> unsortedList = new ArrayList<>();
... // Populate list

for(int i = 0; i < unsortedList.size(); i++)
{
    Collections.sort(unsortedList.get(i));
}

Set<List<HandComponent>> sortedDeDupedSet = new HashSet<>(unsortedList);

// Convert back to list since I need order to matter again later on
List<List<HandComponenet>> sortedDeDupedList = new ArrayList<>(sortedDeDupedSet);

现在,我已经正确实现了“ equals()”和“ hashCode()”方法,并且可以利用List的“ compareTo()”对列表进行排序,从而可以正确地从顶级列表中删除重复项。默认的可比实现。由于我限于Java 7,因此必须使用for循环对列表进行排序确实让人感到有些不好,但是就像我之前说过的那样,最终将列表用于其他目的进行排序非常有用,并且花费了大量的时间和代码。仍然可以避免使用HashSet进行保存,而使用嵌套的for循环则需要手动比较每个List条目。