Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would ".contains" return false for objects pulled directly from a collection?

private Collection<Episode> episodes = new ArrayList<Episode>();

public void checkEpisodes(String string) {
    for(Episode episode : this.episodes){
        System.out.println(string + this.episodes.contains(episode));
    }
}

Why would the above code print false?

I'm using a collection because that's what ormlite allows. I think the issue may be caused by ormlite anyways, because a very similar class has an identical method that prints true.

What I'm trying to do is modify an object returned by:

public Episode findEpisode(int num) {
    checkEpisodes("Find: ");
    for(Episode episode : this.episodes) {
        if(episode.getNumber()==num) {
            return episode;
        }
    }
    return null;
}

But modification of that object isn't saved. I'm assuming because it's not contained within the Collection.

My solution, which works but isn't ideal:

public void modifyEpisode(Episode episode) {
    checkEpisodes("Modify: ");
    for (Iterator<?> it = this.episodes.iterator(); it.hasNext();) {
        if (((Episode) it.next()).getNumber()==episode.getNumber()) {
            it.remove();
            addEpisode(episode.getNumber(), episode.getLink(), episode.getLinkLabel());
        }
    }
}

If you need to see more of my code, just ask, the project is somewhat involved, but it's hosted on sourceforge and I can post a link to it if necessary.

like image 825
Eruza Avatar asked Dec 27 '22 01:12

Eruza


1 Answers

for(Episode episode : this.episodes) {
    System.out.println(string + this.episodes.contains(episode));
}

Why would the above code print false?

In a generic sense, using a standard collection, the only way that I can see if if there are bugs in your equals() method. With most collections, contains() iterates through the collection and uses equals(). The Object.equals() would work so it could be that you have overridden the default equals method and there is a bug in it.

This is also what ORMLite does. If the collection is eager then it calls contains() on the inner ArrayList for the item which would use equals(). If the collection is lazy then it uses the iterator across the table and, again, uses equals() to see if matches.

Edit:

Aha. You state that you have not overridden the equals() method.

One important thing to remember (in terms of ORMLite) is that this is a lazy collection, there is no storage of the objects in memory. When you are iterating across the lazy collection, you get an instance of your Episode object from the database. Then, when you call contains(), it iterates again through the collection creating new Episode objects from the database. It tries to compare the two objects but they will never be equal if you are using Object.equals() because there are not the same object reference.

You must to override the equals() method to get contains() to work for lazy collections.

Also, although your post is probably a simplification of your code, you might consider pulling the lazy collection into an array and then iterating across that. You can't do a contains() on the array but if you needed to search the array, you wouldn't be iterating twice through the database.

Edit:

So the solution turned out to be more complicated. Turns out that the OP had a Show object, with an eager collection of Season objects, each with another eager collection of Episode objects. By default, whenever ORMLite has nested eager collections the inner one is turned into a lazy collection for performance reasons. This is not well documented in version 4.40 unfortunately. You can change this by setting the maxEagerLevel = 2 (or more) on the Show object's collection. See the documentation on maxEagerLevel.

@ForeignCollectionField(eager = false, maxEagerLevel = 2)
ForeignCollection<Season> seasons;
like image 143
Gray Avatar answered Jan 19 '23 00:01

Gray