Issue #3013: Calling List.removeIf makes object dirty even if nothing is removed

Type: Bug ReoprtVersion: 2.9.4_06Priority: NormalStatus: ActiveReplies: 1
#1

Hi! If I have an entity that contains an ArrayList, for example:

@Entity
private static class ExampleEntity {
    
    private List<String> list = new ArrayList<>();
    
    public void add(String item) {
        list.add(item);
    }
    
    public void removeIf(Predicate<String> predicate) {
        list.removeIf(predicate);
    }
}

Then calling removeIf at all, even if it doesn't lead to the removal of any items from the list, still marks the object as dirty:

// Object state is: Nontransactional-Clean
entity.removeIf(item -> false);
// Object state is: Persistent-Dirty

When we use removeIf on a large number of objects, for which only a small % actually causes a removal of any items, it significantly slows down the commit. We have to work around it either by replacing removeIf with an Iterator, or by having an additional check if anything in the list would match the predicate before calling removeIf.

Would it be possible to mark the object as dirty only if the call to removeIf actually modifies it? Thanks.

#2

Thank you for this report. removeIf is currently processed by the Enhancer in the same way as remove, so this should be fixed, and it has now been added to the issue tracking.

As a temporary workaround, consider adding the following static method to your project:

public static <E> boolean removeIf(Collection<E> collection, Predicate<? super E> filter) {
    Objects.requireNonNull(collection);
    Objects.requireNonNull(filter);

    boolean removed = false;
    final Iterator<E> it = collection.iterator();
    while (it.hasNext()) {
        if (filter.test(it.next())) {
            it.remove();
            removed = true;
        }
    }
    return removed;
}

Using this "clone" of removeIf in enhanced code will mark objects as dirty only if remove is actually called.

ObjectDB Support

Reply