4

I'm trying to understand why ArrayList.indexOf() returns -1?

I created a simple class in which I implemented custom equals so that I could compare instances of a class with a string.

public class Currency {
    final private String code;
    final private String unicodeHex;
    final private String name;

    public Currency(String code, String unicodeHex, String name) {
        this.code = code;
        this.unicodeHex = unicodeHex;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public String getSymbol() {
        return unicodeHex;
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;
        if (o instanceof Currency) {
            Currency currency = (Currency) o;
            return code.equals(currency.code);
        } else if (o instanceof String) {
            String currency = (String) o;
            return code.equals(currency);
        } else return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }

    @Override
    public String toString() {
        return "Currency{" +
                "code='" + code + '\'' +
                ", unicodeHex='" + unicodeHex + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

Now I need to find an instance of the class in the ArrayList.

List<Currency>  currencies = new ArrayList<>(Arrays.asList(
                new Currency("USD","0024","United States Dollar"),
                new Currency("EUR","20ac","Euro Member Countries")));

Log.d(TAG, currencies.toString());
Log.d(TAG,Integer.toString(currencies.indexOf("USD")));
2019-02-13 15:41:50.082 D/CurrencySelector: [Currency{code='USD', unicodeHex='0024', name='United States Dollar'}, Currency{code='EUR', unicodeHex='20ac', name='Euro Member Countries'}]
2019-02-13 15:41:50.082 D/CurrencySelector: -1

I am getting: -1

But:

Currency currency = new Currency("USD","0024","United States Dollar");

Log.d(TAG,Boolean.toString(currency.equals("USD")));
2019-02-13 15:41:50.082 D/CurrencySelector: true

the equals returns true as it should.

ArrayList.indexOf uses equals as it should:

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
RoMas
  • 43
  • 3
  • 3
    Hint: `currency.equals("USD")` returns `true` but `"USD".equals(currency)` will **not** return `true`. – Joachim Sauer Feb 13 '19 at 13:52
  • 3
    Your equals is not following the equality contract since string.equals(currency) is false – Minn Feb 13 '19 at 13:52
  • 1
    At least related: https://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java – T.J. Crowder Feb 13 '19 at 13:54

1 Answers1

5

While currency.equals("USD") may return true (due to your implementation), "USD".equals(currency) will never return true, since it uses the equals implementation of the String class, which requires the compared instance to be of type String.

Therefore, currencies.indexOf("USD") will return -1, since o.equals(elementData[i]) calls String's equals, and elementData[i] is not a String.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • This is what happens. List.indexOf uses the equals method of the input. Also I would like to mention that equals should NEVER compare different objects and return true. – kevintjuh93 Feb 13 '19 at 13:52
  • 1
    @kevintjuh93 what else would you compare? identical objects? – bkis Feb 13 '19 at 14:00
  • @Eran Thanks! Did not notice this. In my case, I can use: currencies.indexOf(new Currency("USD",null,null)); But I wanted a more beautiful implementation :) – RoMas Feb 13 '19 at 14:09