diff --git a/src/main/java/it/aboutbits/springboot/toolbox/util/MapByIdentity.java b/src/main/java/it/aboutbits/springboot/toolbox/util/MapByIdentity.java new file mode 100644 index 0000000..cfff13a --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/util/MapByIdentity.java @@ -0,0 +1,51 @@ +package it.aboutbits.springboot.toolbox.util; + +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import it.aboutbits.springboot.toolbox.type.identity.Identified; +import org.jspecify.annotations.NullMarked; +import org.springframework.data.util.Streamable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@NullMarked +public class MapByIdentity, E extends Identified> extends HashMap { + public MapByIdentity(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public MapByIdentity(int initialCapacity) { + super(initialCapacity); + } + + public MapByIdentity() { + super(); + } + + public MapByIdentity(Map m) { + super(m); + } + + public static , E extends Identified> MapByIdentity of(Collection items) { + var map = new MapByIdentity(items.size()); + items.forEach(item -> map.put(item.getId(), item)); + return map; + } + + public static , E extends Identified> MapByIdentity of(Stream items) { + var collected = items.collect(Collectors.toMap( + Identified::getId, + Function.identity() + )); + + return new MapByIdentity<>(collected); + } + + public static , E extends Identified> MapByIdentity of(Streamable items) { + return of(items.toList()); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/util/MapByIdentityTest.java b/src/test/java/it/aboutbits/springboot/toolbox/util/MapByIdentityTest.java new file mode 100644 index 0000000..b13a52b --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/util/MapByIdentityTest.java @@ -0,0 +1,139 @@ +package it.aboutbits.springboot.toolbox.util; + +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import it.aboutbits.springboot.toolbox.type.identity.Identified; +import lombok.Getter; +import org.jspecify.annotations.NullMarked; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.data.util.Streamable; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +@NullMarked +class MapByIdentityTest { + @Nested + class Of { + @Test + void collection() { + // given + var id1 = new TestEntity.ID(1L); + var id2 = new TestEntity.ID(2L); + + var entity1 = new TestEntity(id1); + var entity2 = new TestEntity(id2); + + var entities = List.of(entity1, entity2); + + // when + var result = MapByIdentity.of(entities); + + // then + assertThat(result).hasSize(2) + .containsEntry(id1, entity1) + .containsEntry(id2, entity2); + } + + @Test + void stream() { + // given + var id1 = new TestEntity.ID(1L); + var id2 = new TestEntity.ID(2L); + + var entity1 = new TestEntity(id1); + var entity2 = new TestEntity(id2); + + var entityStream = Stream.of(entity1, entity2); + + // when + var result = MapByIdentity.of(entityStream); + + // then + assertThat(result).hasSize(2) + .containsEntry(id1, entity1) + .containsEntry(id2, entity2); + } + + @Test + void streamable() { + // given + var id1 = new TestEntity.ID(1L); + var id2 = new TestEntity.ID(2L); + + var entity1 = new TestEntity(id1); + var entity2 = new TestEntity(id2); + + var streamable = Streamable.of(entity1, entity2); + + // when + var result = MapByIdentity.of(streamable); + + // then + assertThat(result).hasSize(2) + .containsEntry(id1, entity1) + .containsEntry(id2, entity2); + } + + @Test + void emptyCollection() { + // given + var entities = List.of(); + + // when + var result = MapByIdentity.of(entities); + + // then + assertThat(result).isEmpty(); + } + + @Test + void emptyStream() { + // given + var emptyStream = Stream.empty(); + + // when + var result = MapByIdentity.of(emptyStream); + + // then + assertThat(result).isEmpty(); + } + + @Test + void emptyStreamable() { + // given + var emptyStreamable = Streamable.empty(); + + // when + var result = MapByIdentity.of(emptyStreamable); + + // then + assertThat(result).isEmpty(); + } + } + + @Getter + private static class TestEntity implements Identified { + private final ID id; + + TestEntity(ID id) { + this.id = id; + } + + public record ID( + Long value + ) implements EntityId, Comparable { + @Override + public String toString() { + return String.valueOf(value()); + } + + @Override + public int compareTo(ID other) { + return Long.compare(this.value, other.value); + } + } + } +}