1

Run into abit of trouple with my code, want to check if sharedMemory is present in entire deck(it is) and have no duplicates of an object. I am getting false with this code and I don't really know why, any help would be appreciated. rank and suit together form a card object. rank and card are enumerators with values. Note: when i use 'return Arrays.asList(entireDeck).containsAll(sharedMemory)' on its own it does show true, but the messy if part is trying to check for duplicates

 public static boolean isFull(){
        Card[] entireDeck = Deck.fillDeck();
        sharedMemory =Arrays.asList(Deck.fillDeck());
        int i=0,duplicates=0, position =0, original;
        for(Card c:entireDeck){
            Card f = new Card(c.rank, c.suit);
            original=i;
            if (f.rank.equals(sharedMemory.get(i).rank)&&f.suit.equals(sharedMemory.get(i).suit)){
                duplicates+=1;
                position=i;
                for(i=0; i<position;i++) {
                    if (f.rank.equals(sharedMemory.get(i).rank)&&f.suit.equals(sharedMemory.get(i).suit)) 
               {
                        duplicates += 1;
                        return false;
                    }
                }
                for (i=position+1; i<52; i++){
                    if (f.rank.equals(sharedMemory.get(i).rank)&&f.suit.equals(sharedMemory.get(i).suit)) 
              {
                        duplicates += 1;
                        return false;
                    }
                }
                if(duplicates>1){
                    return false;
                }
                else{
                    i=original;
                }

            }
            i=original;
            i++;
        }
        return Arrays.asList(entireDeck).containsAll(sharedMemory);
    }

Thanks :)

BigJamo
  • 23
  • 5
  • What is the data type of the `rank` and `suit`? Are they Strings, or primitives like integers, etc, or are they an object? Note that when you compare (`.equals`) two separate objects, that even if the objects contain exactly the same data they will not match. See here: https://stackoverflow.com/questions/17064661/why-are-two-objects-with-same-data-not-equal-while-using-equals-method – sorifiend Jan 28 '21 at 23:56
  • @sorifiendsorry rank and suit together form a card object. rank and card are enumerators with values. Note: when i use 'return Arrays.asList(entireDeck).containsAll(sharedMemory)' on its own it does show true, but the messy if part is trying to check for duplicates – BigJamo Jan 29 '21 at 00:01

2 Answers2

1

The easiest way is to use a Set<Card>. If the return value of adding an object to a set is false, then it means the set already contains it, thus a duplicate. For this to work the Card class must override both hashCode and equals.

Set<Card> set = new HashSet<>();
for(Card card : cardArray) {
    if (!set.add(card)) { // if false then !false is true so signal duplicate.
        System.out.println("Duplicate of " + card + " found);
        break;
    }
}

You can always sort the Cards and then do a one to one comparison to see if they are equal. Comparing adjacent cards of a sorted deck can also detect duplicates.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • 'must overrive both HashCode and equals' is not really true, it SHOULD. If you're using the same (identical, ==) objects, you can leave the Card class as is. That being said, check out https://stackoverflow.com/a/27609/1932011 for a good and detailed answer that keeps you out of troubles. – JayC667 Jan 29 '21 at 00:20
  • Not a good idea to assume that the two of the same cards are the same object. – WJS Jan 29 '21 at 00:25
  • An `if` is not an assumption ;-) – JayC667 Jan 29 '21 at 00:28
  • @JayC667 Thanks for the info, unfortunately this is for a homework, and cannot use Apache libraries which HashCodeBuilder is in...any way to get around this? Also, I have many other classes that use .equals and they work fine, why would this be different? – BigJamo Jan 29 '21 at 00:28
  • @BigJamo You do not need to use those classes/libraries. The article is more about what to keep an eye out for. If your Card class has both 'rank' and 'suit' and these are the only two factors in each object that do not change and make it unique, then focus on those. The basic problem of "how to implement hashCode and equals" is so omnipresent that you will find plenty of answers out there. OR, you add the full code of your Card class, then you can also get a detailed solution for that, just as WJS gave you a solution to the code you posted earlier. – JayC667 Jan 29 '21 at 00:35
  • Oh by the way, good IDEs also create hashCode and equals code for you if find the right menus/quick-fixes ;-) – JayC667 Jan 29 '21 at 00:37
  • 1
    @BigJamo. If you **always** use the same object for any given card then you do not need to override `hashCode` or `equals` as was implied by @JayC667. But if you create two cards, say "Ace of Diamonds" and compare them, they will be considered unequal unless you override equals and hashCode to acutally compare their values. – WJS Jan 29 '21 at 00:38
1

Here is one example of how this could be achieved using an integer array. For your use case you would want to implement a Comparator for sorting and ordering.

I decided to show an approach which modify the input arrays during the check (by sorting them each run). This allows for moderate performance improvements, however I doubt that your use case will be using a large enough dataset for it to be a consideration.

// Checks if a is a subset of b and does not contain duplicates
public static boolean subsetOf(int[] a, int[] b) {
    Arrays.sort(a);
    Arrays.sort(b);

    int aIdx = 0;
    int bIdx = 0;

    while (aIdx < a.length && bIdx < b.length) {
        if (a[aIdx] == b[bIdx]) {
            aIdx++;
            bIdx++;

            // Check for duplicate when incrementing index
            if (aIdx + 1 < a.length && a[aIdx] == a[aIdx + 1]) {
                return false;
            }
        } else if (b[bIdx] < a[aIdx]) {
            // We need to keep moving through b to find next a
            bIdx++;
        } else {
            // We missed an element in a
            return false;
        }
    }

    // Verify that we found all elements in a
    return aIdx == a.length;
}

For your use case, I would recommend something like this for readability.

import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;

public static boolean subsetOf(Card[] a, Card[] b) {
    Set<Card> aSet = new Hashset<>();
    Set<Card> bSet = new Hashset<>();

    aSet.addAll(Arrays.asList(a));
    bSet.addAll(Arrays.asList(b));

    return aSet.size() == a.length && bSet.containsAll(aSet);
}

In order to use this method, make sure to implement hashCode and equals in Card. You don't need to do anything fancy or import any extra libraries. The important part is that it returns a different number for every rank/suit combination.

public class Card {

    // Gives a unique number for each card
    @Override
    public int hashCode() {
        return 4 * rank + suitNum;
    }

    // Check if this card is the same as another object
    @Override
    public boolean equals(Object other) {
        if (other instanceof Card) {
            Card otherCard = (Card) other;
            return rank == otherCard.rank && suitNum == otherCard.suitNum;
        }
        return false;
    }
}
Locke
  • 7,626
  • 2
  • 21
  • 41