3

I'm using stack in my program but I have a problem when program is trying to check what element contains in stack. I'm using array of integer in Stack. Short example is:

        Stack<int[]> myStack = new Stack<int[]>();
        myStack.push(new int[]{1,2});
        myStack.push(new int[]{1,3});
        myStack.push(new int[]{1,4});
        if (myStack.contains(new int[]{1,3})) {
            System.out.println("YES");
        } else {
            System.out.println("NO");
        }

Now it was printed "NO". How could I do to get "YES" ? I know that the problem is that I'm not using same object but in the reality my program is much larger and I can't to use this

int[] myInteger = new int[]{1,3};
myStack.push(myInteger);
myStack.contains(myInteger);
Martynas
  • 2,545
  • 7
  • 30
  • 36
  • The class `Stack` (just like `Vector` and `HashTable`) are just hangovers from long time ago and IMHO should shouldn't be used anymore. Unfortunately, there's no stack in the modern part of the Collections framework, but you can create one by subclassing ArrayList and implementing two trivial methods. – maaartinus Apr 20 '11 at 02:07

6 Answers6

6

That's simple: int[] get compared by identity,

new int[]{1,3}.equals(new int[]{1,3})

returns false. There are many solutions:

  • Use List<Integer> instead. That's quite inefficient, but may suffice.
  • Wrap the int[] into an object and implement equals and hashCode.
  • Use a primitive collection library like trove4j which provides things similar to List<int>
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 1
    +1 - but you should probably warn of the pitfalls of a someone mutating the wrapped `int[]`. – Stephen C Apr 19 '11 at 23:35
  • 1
    Also, the OP's example could actually be a case of "object denial", and what he might really need is a domain-specific class rather than a generic wrapped `int[]`. – Stephen C Apr 19 '11 at 23:37
  • 1
    +1 to both comments. In case he really needs a wrapped `int[]`, he should use [defensive copy](http://en.wikipedia.org/wiki/http://www.javapractices.com/topic/TopicAction.do?Id=15). – maaartinus Apr 20 '11 at 00:13
1

To get it to print "YES" you need to make the stack be of some object that has defined equals() and hashCode() such that two arrays with identical elements will be considered equal.

So something like this (which is just dashed off and probably could be better):

public class ArrayHolder
{
    private int[] theArray;

    public ArrayHolder(int[] theArray) {
       this.theArray = theArray;
    }

    public array() {
        return theArray;
    }

    public boolean equals(Object o) {
        // Code that will return true if
        // o is an int[] that contains the
        // same elements as this
    }

    public int hashCode() {
        // code that will return a hash
        // based on the array elements so
        // that arrays with the same elements
        // will have the same hash
    }
}

And then have your Stack be Stack<ArrayHolder>.

QuantumMechanic
  • 13,795
  • 4
  • 45
  • 66
1

One option is to use a wrapper, like

class ArrayWrapper {
    int[] data;

    public boolean equals(Object o) {
        if (o instanceof ArrayWrapper)
            return Arrays.equals(data, ((ArrayWrapper) o).data);
        return false;
    }

    public int hashCode() {
        return Arrays.hashCode(data);
    }
}

Another fix would be to override contains, as in

class SmartStack extends Stack<int[]> {
    @Override
    public boolean contains(Object o) {
        ...
    }
}
Daniel Lubarov
  • 7,796
  • 1
  • 37
  • 56
  • The SmartStack is quite a bad hack. Are you sure, there are no more methods to be overridden? – maaartinus Apr 20 '11 at 02:10
  • No, we'd need to override nearly a dozen methods if we wanted to ensure that all of Stack's methods worked safely. So admittedly it's not a great solution. But it does avoid the overhead of wrapper objects or dynamic lists. I might do it if I had many arrays in a stack and wasn't concerned with "idiot proofing" the code. – Daniel Lubarov Apr 20 '11 at 05:05
1

the int[] array objects have no way of knowing if two arrays are equal because they are in effect just pointers, and since they are pointing to two different arrays, they are considered unequal. The reason why the pointers are used to compare equality is because the Object class defines the most rudimentary equals method in such a fashion.

You need to create a simple class to encapsulate your array. Here is a simple one anyone could understand.

class IntArray {
    int[] myArray;
    public IntArray () {
        myArray = new int[0];
    }
    public IntArray (int [] array) {
        myArray = array;
    }
    /**
     * This method is for accessing the array.
     * The array CAN be modified using this method.
     */
    public int[] getArray() {
        return myArray;
    }
    /**
     * Uses built-in hashCode generating method
     * within the Arrays class.
     * Importing java.util.Arrays is necessary.
     */
    public int hashCode() {
        return Arrays.hashCode(myArray);
    }
    public boolean equals(Object o) {
        if (!(o instanceof IntArray))
            return false;

        //Should use Arrays.equals(o.myArray, myArray);

        if (o.myArray.length != myArray.length)
            return false;
        else {
            for (int i = 0; i < myArray.length; i++) {
                if (myArray[i] != o.myArray[i]) {
                    return false;
                }
            }
            return true;
        }
    }
}

Once this is done, you can easily do something like you have done before:

    Stack<In> myStack = new Stack<int[]>();
    myStack.push( new IntArray (new int[]{1,2}) );
    myStack.push( new IntArray (new int[]{1,3}) );
    myStack.push( new IntArray (new int[]{1,4}) );
    if (myStack.contains( new IntArray (new int[]{1,3})) ) {
        System.out.println("YES");
    } else {
        System.out.println("NO");
    }

That should definitely work.

Kaushik Shankar
  • 5,491
  • 4
  • 30
  • 36
0

The contains() method relies on the result of equals() - if you want full control, subclass and over-ride equals() to mean what you intend.

exception
  • 569
  • 7
  • 20
0

First you have to define what "contains" means. The docs for Stack.contains say:

Returns: true if and only if the specified object is the same as a component in this vector, as determined by the equals method.

(Stack extends Vector)

So you could create your own class that has an array of ints as an instance variable and override "equals" on that class to implement what ever that means in your case. Just make sure you do this properly. Information on how to do that is everwhere

Community
  • 1
  • 1
ax123man
  • 578
  • 5
  • 17