diff --git a/src/main/java/org/cactoos/collection/CollectionEnvelope.java b/src/main/java/org/cactoos/collection/CollectionEnvelope.java index f024782ad5..9da842e51d 100644 --- a/src/main/java/org/cactoos/collection/CollectionEnvelope.java +++ b/src/main/java/org/cactoos/collection/CollectionEnvelope.java @@ -27,19 +27,23 @@ import java.util.Iterator; import org.cactoos.Scalar; import org.cactoos.iterator.Immutable; +import org.cactoos.scalar.And; +import org.cactoos.scalar.Folded; +import org.cactoos.scalar.InheritanceLevel; +import org.cactoos.scalar.Or; +import org.cactoos.scalar.SumOfIntScalar; import org.cactoos.scalar.UncheckedScalar; /** * Base collection. * *

There is no thread-safety guarantee.

- * * @param Element type - * @since 0.23 * @todo #844:30min Implement methods equals and hashCode for this class. - * Implementation should rely on the items of the nested collection, but not - * on default JVM impl. Class {@link org.cactoos.map.MapEnvelope} can be used - * as an example. + * Implementation should rely on the items of the nested collection, but not + * on default JVM impl. Class {@link org.cactoos.map.MapEnvelope} can be used + * as an example. + * @since 0.23 * @checkstyle AbstractClassNameCheck (500 lines) */ @SuppressWarnings( @@ -147,4 +151,48 @@ public String toString() { return this.col.value().toString(); } + @Override + public final boolean equals(final Object other) { + return new UncheckedScalar<>( + new And( + new Or( + () -> new InheritanceLevel( + other.getClass(), Collection.class + ).value() > -1, + () -> new InheritanceLevel( + other.getClass(), CollectionEnvelope.class + ).value() > -1 + ), + () -> { + final Collection compared = (Collection) other; + return this.size() == compared.size(); + }, + () -> { + final Iterable compared = (Iterable) other; + final Iterator iterator = compared.iterator(); + return new UncheckedScalar<>( + new And( + (X input) -> input.equals(iterator.next()), + this + ) + ).value(); + } + ) + ).value(); + } + + // @checkstyle MagicNumberCheck (30 lines) + @Override + public final int hashCode() { + return new UncheckedScalar<>( + new Folded<>( + 42, + (hash, entry) -> new SumOfIntScalar( + () -> 37 * hash, + entry::hashCode + ).value(), + this + ) + ).value(); + } } diff --git a/src/main/java/org/cactoos/list/ListEnvelope.java b/src/main/java/org/cactoos/list/ListEnvelope.java index 848c08cbcd..3f5391a1d5 100644 --- a/src/main/java/org/cactoos/list/ListEnvelope.java +++ b/src/main/java/org/cactoos/list/ListEnvelope.java @@ -113,16 +113,6 @@ public final List subList(final int start, final int end) { return this.list.value().subList(start, end); } - @Override - public boolean equals(final Object obj) { - return this.list.value().equals(obj); - } - - @Override - public int hashCode() { - return this.list.value().hashCode(); - } - @Override public String toString() { return this.list.value().toString(); diff --git a/src/test/java/org/cactoos/collection/CollectionEnvelopeTest.java b/src/test/java/org/cactoos/collection/CollectionEnvelopeTest.java index 58d4f27e74..fa17779ff4 100644 --- a/src/test/java/org/cactoos/collection/CollectionEnvelopeTest.java +++ b/src/test/java/org/cactoos/collection/CollectionEnvelopeTest.java @@ -26,6 +26,10 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import org.cactoos.list.ListOf; +import org.hamcrest.MatcherAssert; +import org.hamcrest.core.IsEqual; +import org.hamcrest.core.IsNot; import org.junit.Test; /** @@ -37,6 +41,7 @@ * Iterator for IterableEnvelope `iterator()` method. * @checkstyle JavadocMethodCheck (500 lines) */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") public final class CollectionEnvelopeTest { @Test(expected = UnsupportedOperationException.class) public void returnsIteratorWithUnsupportedRemove() { @@ -51,4 +56,99 @@ public void returnsIteratorWithUnsupportedRemove() { iterator.next(); iterator.remove(); } + + @Test + public void notEqualToObjectOfAnotherType() { + MatcherAssert.assertThat( + "Collection is equal to object of different type", + new CollectionOf<>(), + new IsNot<>(new IsEqual<>("a")) + ); + } + + @Test + public void notEqualToCollectionOfDifferentSize() { + MatcherAssert.assertThat( + "Collection is equal to a collection of different size", + new CollectionOf<>(), + new IsNot<>(new IsEqual<>(new CollectionOf<>("b"))) + ); + } + + @Test + public void notEqualToCollectionOfDifferentElements() { + MatcherAssert.assertThat( + "Collection is equal to a collection with different content", + new CollectionOf<>("a", "b"), + new IsNot<>(new IsEqual<>(new CollectionOf<>("a", "c"))) + ); + } + + @Test + public void equalToCollectionWithIdenticalContent() { + MatcherAssert.assertThat( + "Collection is not equal to a collection with identical content", + new CollectionOf<>("val1", "val2"), + new IsEqual<>(new CollectionOf<>("val1", "val2")) + ); + } + + @Test + public void equalToListWithIdenticalContent() { + MatcherAssert.assertThat( + "Collection not equal to a list with identical content", + new CollectionOf<>("a"), + new IsEqual<>(new ListOf<>("a")) + ); + } + + @Test + public void equalToDerivedCollection() { + MatcherAssert.assertThat( + "Collection not equal to derived collection with identical content", + new CollectionOf<>("a"), + new IsEqual<>(new CollectionEnvelopeTest.CustomCollection("a")) + ); + } + + @Test + public void equalToEmptyCollection() { + MatcherAssert.assertThat( + "Empty collection not equal with empty collection", + new CollectionOf<>(), + new IsEqual<>(new CollectionOf<>()) + ); + } + + @Test + public void hashCodeEqual() { + MatcherAssert.assertThat( + "HashCode returns different results for same entries", + new CollectionOf<>("a", "b").hashCode(), + new IsEqual<>(new CollectionOf<>("a", "b").hashCode()) + ); + } + + @Test + public void differentHashCode() { + MatcherAssert.assertThat( + "HashCode returns identical results for different entries", + new CollectionOf<>("a", "b").hashCode(), + new IsNot<>(new IsEqual<>(new CollectionOf<>("b", "a").hashCode())) + ); + } + + /** + * Custom collection. + */ + private static class CustomCollection extends CollectionEnvelope { + + /** + * Ctor. + * @param elements String elements + */ + CustomCollection(final String... elements) { + super(() -> new CollectionOf<>(elements)); + } + } }