Examples
These examples show the most common ways to use Hermes.
Detect Updates in a Collection
Use CollectionItemChangeDetector when order does not matter.
This detector reports inserted, removed, and updated items, but it does not report moves.
import com.irurueta.hermes.CollectionItemChangeAction;
import com.irurueta.hermes.CollectionItemChangeDetector;
import com.irurueta.hermes.UpdatedCollectionItemChange;
import java.util.List;
record User(long id, String displayName, boolean active) { }
var oldUsers = List.of(
new User(1, "Ada", true),
new User(2, "Grace", true));
var newUsers = List.of(
new User(1, "Ada Lovelace", true),
new User(2, "Grace", true));
var detector = new CollectionItemChangeDetector<User>(
(left, right) -> left.id() == right.id(),
(left, right) -> left.displayName().equals(right.displayName())
&& left.active() == right.active());
var changes = detector.detectChanges(newUsers, oldUsers);
for (var change : changes) {
if (change.getAction() == CollectionItemChangeAction.UPDATED) {
var updated = (UpdatedCollectionItemChange<User>) change;
System.out.println(updated.getOldItem() + " -> " + updated.getNewItem());
}
}
Replay Sequential List Changes
Sequential detectors are a good fit when you want to apply the returned operations to an existing ordered list.
import com.irurueta.hermes.InsertedListItemChange;
import com.irurueta.hermes.ListItemChange;
import com.irurueta.hermes.MovedListItemChange;
import com.irurueta.hermes.RemovedListItemChange;
import com.irurueta.hermes.SequentialListItemChangeDetector;
import com.irurueta.hermes.UpdatedListItemChange;
import java.util.ArrayList;
import java.util.List;
record Row(String id, String label) { }
var oldRows = List.of(
new Row("a", "Alpha"),
new Row("b", "Beta"),
new Row("c", "Gamma"));
var newRows = List.of(
new Row("c", "Gamma"),
new Row("b", "Beta v2"),
new Row("d", "Delta"));
var detector = new SequentialListItemChangeDetector<Row>(
(left, right) -> left.id().equals(right.id()),
(left, right) -> left.label().equals(right.label()));
var changes = detector.detectChanges(newRows, oldRows);
var working = new ArrayList<>(oldRows);
for (ListItemChange change : changes) {
switch (change.getAction()) {
case REMOVED -> {
var removed = (RemovedListItemChange<Row>) change;
working.remove(removed.getOldPosition());
}
case INSERTED -> {
var inserted = (InsertedListItemChange<Row>) change;
working.add(inserted.getNewPosition(), inserted.getNewItem());
}
case MOVED -> {
var moved = (MovedListItemChange<Row>) change;
var row = working.remove(moved.getOldPosition());
working.add(moved.getNewPosition(), row);
}
case UPDATED -> {
var updated = (UpdatedListItemChange<Row>) change;
working.set(updated.getPosition(), updated.getNewItem());
}
}
}
Use Comparable Items
When items implement ComparableItem, Hermes can use the item methods directly.
import com.irurueta.hermes.ComparableItem;
import com.irurueta.hermes.ComparableCollectionItemChangeDetector;
record Account(String number, String owner, String status)
implements ComparableItem<Account> {
@Override
public boolean equals(Object other) {
return other instanceof Account account
&& number.equals(account.number);
}
@Override
public int hashCode() {
return number.hashCode();
}
@Override
public boolean equalContent(Account other) {
return owner.equals(other.owner) && status.equals(other.status);
}
}
var detector = new ComparableCollectionItemChangeDetector<Account>();
var changes = detector.detectChanges(newAccounts, oldAccounts);
The important rule is that equals(Object) identifies the same logical item.
It should not compare every field if those fields are supposed to produce UPDATED changes.