我有一个递归函数,该函数生成一个列表列表,该列表跟踪纸牌游戏的有效手形组合:
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,而我确实需要允许它们。
答案 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条目。