0

i'm making an app that makes an API request and gets back a list of Games, with one of the fields being the release date in seconds Timestamp. When i make the call for e.g. call of duty the sorting goes well, but if i do so for like zelda it throws java.lang.IllegalArgumentException: Comparison method violates its general contract!

I filter out the ones that return a null release date, but I don't know why with some games it just don't work.

Here is the code for the sorting

Collections.sort(mGames, new Comparator<Game>() {
                    @Override
                    public int compare(Game o1, Game o2) {
                        if(o1.getDate() != null && o2.getDate() != null)
                            return Integer.valueOf(o2.getDate()).compareTo(Integer.valueOf(o1.getDate()));
                        return 1;
                    }
                });

and the responses (some of them) for spyro search (which works)

2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 941500800 Name  = Spyro 2: Ripto's Rage!

2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1542067200 Name = Spyro Reignited Trilogy 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1086220800 Name = Spyro Orange: The Cortex Conspiracy 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1004313600 Name = Spyro: Season of Ice 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 972432000 Name = Spyro: Year of the Dragon 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1067212800 Name = Spyro: Attack of the Rhynocs 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1191283200 Name = The Legend of Spyro: The Eternal Night 2020-06-04 01:07:22.723 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1542067200 Name = Spyro + Crash Remastered Game Bundle 2020-06-04 01:07:22.724 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = null Name = Spyro Fusion 2020-06-04 01:07:22.724 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = null Name = Spyro Superpack

and for e.g. pokemon, just some, (which crashes and caused the java.lang.IllegalArgumentException: Comparison method violates its general contract!

2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1573776000 Name = Pokémon Sword & Pokémon Shield Double Pack 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1129766400 Name = Pokémon Trozei! 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1366761600 Name = Pokémon Rumble U 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1141603200 Name = Pokemon Link! 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 913939200 Name = Pokémon Trading Card Game 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = null Name = Pokemon-e: Expedition 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1413849600 Name = Camp Pokémon 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 943142400 Name = Pokémon Gold 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 924048000 Name = Pokémon Pinball 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 1075334400 Name = Pokémon FireRed 2020-06-04 01:08:24.100 26928-26928/it.unimib.disco.gruppoade.gamenow D/SearchActivity: onResponse: Release date = 925430400 Name = Pokémon Stadium

as you can see they both have some that contain the date and some null. But spyro works and pokemon doesn't and I can't understand why.

Thank you

2 Answers 2

1

The sorting algorithm in Java auto-detects violations of the contract of the Comparator.

You can read it in the documentation of Comparator/Comparable.

  1. The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y. (This implies that compare(x, y) must throw an exception if and only if compare(y, x) throws an exception.)

  2. The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0.

  3. Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.

You have violated the requirements by returning 1 when either of dates are null.

So consider:

// a.getDate() == null
// b.getDate() != null

Comparator yourComparator = ...;
yourComparator.compare(a, b); // returns 1 - that means b > a
yourComparator.compare(b, a); // returns 1 - that means a > b

Which is impossible and violates the first rule.

Change the comparator so that it adheres to the rules when either of date is null so it's behaving consistently.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your response, but I still don't understand where and how to change the comparator
1

To Krzysztof

I tried changing it like so

 Collections.sort(mGames, new Comparator<Game>() {
                    @Override
                    public int compare(Game o1, Game o2) {
                        if(o1.getDate() != null && o2.getDate() != null)
                            return Integer.valueOf(o2.getDate()).compareTo(Integer.valueOf(o1.getDate()));
                        if(o1.getDate() == null && o2.getDate() == null)
                            return 0;
                        if(o1.getDate() == null)
                            return -1;
                        return 1;
                    }
                });

and it works. I'm still open to advices

1 Comment

Don’t use Integer, it creates a year 2038 problem. I suggest Long.compare(Long.parseLong(o1.getDate()), Long.parseLong(o2.getDate())).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.