一只斗牛犬
您可以将int[]数组包装在一个特殊的容器中,该容器会覆盖equals和hashCode:public class IntArrayHolder { private final int[] array; public IntArrayHolder(int[] array) { this.array = array; } public int[] getArray() { return array; } @Override public boolean equals(Object object) { if (this == object) return true; if (!(object instanceof IntArrayHolder)) return false; IntArrayHolder that = (IntArrayHolder) object; return Arrays.equals(array, that.array); } @Override public int hashCode() { return Arrays.hashCode(array); }}并使用Set<IntArrayHolder>而不是Set<int[]>:Set<IntArrayHolder> set = new HashSet<>();set.add(new IntArrayHolder(new int[]{0}));set.add(new IntArrayHolder(new int[]{0}));set.add(new IntArrayHolder(new int[]{1}));set.forEach(elm -> System.out.println(Arrays.toString(elm.getArray()))); // prints [1][0]您还可以将包装和解包装封装在特殊的泛型类中,并以这种方式使用它们:Set<int[]> set = new MappedSet<>(new HashSet<>(), Mapper.from( IntArrayHolder::getArray, IntArrayHolder::new));set.add(new int[]{0});set.add(new int[]{0});set.add(new int[]{1});set.forEach(elm -> System.out.println(Arrays.toString(elm))); // prints [1][0]这里是Mapper、MappedSet、MappedCollection、MappedIterable和MappedIterator:MappedArray/** * <h1>Mapper</h1> * Represents a two way function which can be used for adapting * {@code Type<A>} to {@code Type<B>} where {@code Type} is invariant * generic type. * * <h2>Invertibility</h2> * <p> * If this mapper isn't bounded result of performing {@code out(in(x))} should * be similar to {@code x}, no guarantees are made about their equality. The same goes * with {@code in(out(x))}. * * <h2>Nullability</h2> * <p> * A mapper always converts {@code null} to {@code null} and non-null references to non-null * references. {@code null} can be passed to {@link #out(A)} and {@link #in(B)} methods, but * should never be passed to {@link #doOut(A)} and {@link #doIn(B)}. * * <h2>Bounded wildcards</h2> * <p> * Bounded wildcards should never be used on {@link A} type parameter, because mapper with bounded * {@link A} can't be accepted by any adapter and is completely useless. * * <p>Using of bounded wildcard on {@link B} type parameter indicates that mapper can perform * operations in only one way see {@link #outMapper(Function)} and {@link #inMapper(Function)}. * * <h2>Unchecked functionality</h2> * A mapper provides unchecked functionality for not generified methods via {@link #uncheckedOut(Object)}, * {@link #uncheckedIn(Object)} and {@link #unchecked()} methods. * <p>Restricting unchecked functionality can be done by using {@link #restrictAClass(Class)} * and {@link #restrictBClass(Class)} methods. * * <h2>Instantiation</h2> * The following factory methods can be used for mapper instantiation: * <ul> * <li>{@link #from(Function, Function)} - two ways mapper</li> * <li>{@link #outMapper(Function)} - only-out mapper</li> * <li>{@link #inMapper(Function)} - only-in mapper</li> * </ul> * <p>This interface can also be implemented from outside. * * @param <A> type parameter of adapted generic instance * @param <B> type parameter of generic adapter */public interface Mapper<A, B> extends Function<A, B> { /** * Adapts return value of adapted instance method to adapter type. * * @param a result of calling adapted instance method * @return result which should be return from adapter * @implNote this method isn't supposed to be overridden, instead {@link #doOut(A)} * method should be implemented for defining mapper behaviour */ @Contract("null -> null; !null -> !null") @Final default @Nullable B out(@Nullable A a) { return a == null ? null : doOut(a); } /** * Adapts input argument of adapter method to adapted instance type. * * @param b argument passed to adapter method * @return argument which should be passed to adapted instance * @implNote this method isn't supposed to be overridden, instead {@link #doIn(B)} * method should be implemented for defining mapper behaviour */ @Contract("null -> null; !null -> !null") @Final default @Nullable A in(@Nullable B b) { return b == null ? null : doIn(b); } /** * Adapts return value of not generified adapted instance method to adapter type, * uses unchecked cast. * * @param a result of calling adapted instance method, is to have {@link A} type * @return result which should be return from adapter * @see #out(A) */ @SuppressWarnings("unchecked") // unchecked is indicated in the method name @Contract("null -> null; !null -> !null") @Final default @Nullable B uncheckedOut(@Nullable Object a) { return out((A) a); } /** * Adapts input argument of not generified adapter method to adapted instance type, * uses unchecked cast. * * @param b argument passed to adapter method, is to have {@link B} type * @return argument which should be passed to adapted instance * @see #in(B) */ @SuppressWarnings("unchecked") // unchecked is indicated in the method name @Contract("null -> null; !null -> !null") @Final default @Nullable A uncheckedIn(@Nullable Object b) { return in((B) b); } /** * Performs adapting of {@link A} to {@link B}. * * @param a input argument, mustn't be {@code null} * @return adaptation result, mustn't be {@code null}. If adaptation has failed {@link IllegalArgumentException} * should be thrown or null-object should be returned. * @apiNote this method isn't supposed to be called, instead {@link #out(A)} method should be used * @implNote this method is supposed to be implemented for defining {@link #out(A)} method behaviour * @see #out(A) */ @NotNull B doOut(@NotNull A a); /** * Performs adapting of {@link B} to {@link A}. * * @param b input argument, mustn't be {@code null} * @return adaptation result, mustn't be {@code null}. If adaptation has failed {@link IllegalArgumentException} * should be thrown or null-object should be returned. * @apiNote this method isn't supposed to be called, instead {@link #in(B)} method should be used * @implNote this method is supposed to be implemented for defining {@link #in(B)} method behaviour * @see #in(B) */ @NotNull A doIn(@NotNull B b); /** * Non-null analog of {@link #out(A)}. * * @param a input argument * @return mapping result * @apiNote {@code mapper::apply} shouldn't be used, instead mapper instance itself * should be used as a {@code Function<A, B>} */ @Override default @NotNull B apply(@NotNull A a) { return out(a); } /** * Non-null analog of {@link #in(B)}. * * @param b input argument * @return mapping result * @apiNote {@code mapper::undo} shouldn't be used, instead {@link #reverse()} * should be used as a {@code Function<B, A>} */ default @NotNull A undo(@NotNull B b) { return in(b); } /** * Returns reverse view of this mapper. * <p>Some mappers may perform reverse view caching, so it's likely that * {@code mapper.reverse() == mapper.reverse()} and {@code mapper.reverse().reverse() == mapper} * will be true. * * @return reverse view of this mapper */ default Mapper<B, A> reverse() { return new Mapper<B, A>() { @Override public @NotNull A doOut(@NotNull B b) { return Mapper.this.doIn(b); } @Override public @NotNull B doIn(@NotNull A a) { return Mapper.this.doOut(a); } @Override public Mapper<A, B> reverse() { return Mapper.this; } }; } /** * Returns this mapper after unchecked casting to required type. * <p>Should be used carefully: sometimes {@link #reverse()}{@code .unchecked()} is required instead * of {@code unchecked()}. * * @param <A2> {@link A} type parameter of returned mapper * @return this instance * @see #uncheckedIn(Object) * @see #uncheckedOut(Object) */ @SuppressWarnings("unchecked") // unchecked is indicated in the method name @Contract(value = "-> this", pure = true) default <A2> Mapper<A2, B> unchecked() { return (Mapper<A2, B>) this; } @SuppressWarnings("unchecked") // unchecked is indicated in the method name default <A2, B2> Mapper<A2, B2> doubleUnchecked() { return (Mapper<A2, B2>) this; } /** * Creates a composed mapper from {@link V} to {@link B} which uses {@code before} * mapper and this mapper in order required by operations. * * @param <V> the first type parameter of {@code before} mapper, and of the * composed mapper * @param before the additional mapper * @return a composed mapper * @see #andThenMapper(Mapper) * @see #compose(Function) */ @Contract(value = "_ -> new", pure = true) default <V> Mapper<V, B> composeMapper(Mapper<V, A> before) { return before.andThenMapper(this); } /** * Creates a composed mapper from {@link A} to {@link C} which uses this * mapper and {@code after} mapper in order required by operations. * * @param <C> the second type parameter of {@code after} mapper, and of the * composed mapper * @param after the additional mapper * @return a composed mapper * @see #composeMapper(Mapper) * @see #andThen(Function) */ @Contract(value = "_ -> new", pure = true) default <C> Mapper<A, C> andThenMapper(Mapper<B, C> after) { return from(a -> after.out(out(a)), c -> in(after.in(c))); } /** * Creates mapper which restricts {@link A} class, so type of {@link #out(A)} operation parameter * is checked before performing operation. * <p>If parameter isn't an {@code instanceof clazz} {@link #uncheckedMap(Object)} is used. * * @param clazz {@link A} class * @return mapper which restricts {@link A} class * @see #restrictBClass(Class) */ @Contract(value = "_ -> new", pure = true) default Mapper<A, B> restrictAClass(Class<A> clazz) { return from(a -> clazz.isAssignableFrom(a.getClass()) ? doOut(a) : uncheckedMap(a), this::doIn); } /** * Creates mapper which restricts {@link B} class, so type of {@link #in(B)} operation parameter * is checked before performing operation. * <p>If parameter isn't an {@code instanceof clazz} {@link #uncheckedMap(Object)} is used. * * @param clazz {@link B} class * @return mapper which restricts {@link B} class * @see #restrictAClass(Class) */ @Contract(value = "_ -> new", pure = true) default Mapper<A, B> restrictBClass(Class<B> clazz) { return from(this::doOut, b -> clazz.isAssignableFrom(b.getClass()) ? doIn(b) : uncheckedMap(b)); } /** * Creates mapper from given functions. * * @param out function for performing {@link #doOut(A)} method * @param in function for performing {@link #doIn(B)} method * @param <A> returned mapper first type parameter * @param <B> returned mapper second type parameter * @return created mapper */ @Contract(value = "_, _ -> new", pure = true) static <A, B> Mapper<A, B> from(Function<? super A, ? extends B> out, Function<? super B, ? extends A> in) { return new Mapper<A, B>() { private @Nullable Mapper<B, A> reverse = null; @Override public @NotNull B doOut(@NotNull A a) { return out.apply(a); } @Override public @NotNull A doIn(@NotNull B b) { return in.apply(b); } @Override public Mapper<B, A> reverse() { return (reverse == null) ? (reverse = Mapper.super.reverse()) : reverse; } }; } /** * Creates mapper that can only normally perform {@link #out(A)} operation. * * <p>The returned mapper is bounded so {@link #in(B)} operation without using <i>unchecked functionality</i> * can only be called with {@code null} argument, according to the contract this will lead to * returning of {@code null}. * * @param out function for performing {@link #doOut(A)} method * @param <A> returned mapper first type parameter * @param <B> returned mapper second type parameter * @return mapper that only provides {@link #out(A)} operation */ @Contract(value = "_ -> new", pure = true) static <A, B> Mapper<A, ? extends B> outMapper(Function<? super A, ? extends B> out) { return from(out, Mapper::uncheckedMap); } /** * Creates mapper that can only normally perform {@link #in(B)} operation. * * <p>The returned mapper is bounded so {@link #out(A)} result can be used only as {@link Object}. * <p>Returned mapper guarantees that {@code out(x) == x} will always be {@code true}. * * @param in function for performing {@link #doIn(B)} method * @param <A> returned mapper first type parameter * @param <B> returned mapper second type parameter * @return mapper that only provides {@link #in(B)} operation */ @Contract(value = "_ -> new", pure = true) static <A, B> Mapper<A, ? super B> inMapper(Function<? super B, ? extends A> in) { return from(Mapper::uncheckedMap, in); } /** * Creates mapper that maps any object to itself. * * @param <T> returned mapper type parameter * @return created mapper */ @Contract(value = "-> new", pure = true) static <T> Mapper<T, T> identity() { return from(Function.identity(), Function.identity()); } /** * Performs unchecked cast of input to required type, * this method may also do some logging. * * <p>This method is supposed to be used when impossible to call {@code Function<? extends A, B>} or * returning {@link Object} {@code Function<A, ? super B>} is needed. * * <p>In some cases it can also be called when {@code a} appeared to be not an {@code instanceof Class<A>}. * * @param a input argument * @param <A> input argument type * @param <B> required type * @return result of unchecked cast * @deprecated unchecked cast */ @SuppressWarnings({"Contract", "unchecked"}) // unchecked is indicated in the method name @Contract("_ -> param1") @Deprecated @WarnOnUse(exceptClasses = Mapper.class) static <A, B> B uncheckedMap(A a) { return (B) a; }}public class MappedSet<E, E2> extends MappedCollection<E, E2> implements Set<E2> { public MappedSet(Set<E> set, Mapper<E, E2> mapper) { super(set, mapper); } public static <E, E2> Set<E2> map(Set<E> inst, Mapper<E, E2> mapper) { return new MappedSet<>(inst, mapper); } public static <E, E2> Set<? extends E2> mapOut(Set<? extends E> inst, Function<? super E, ? extends E2> mapper) { return map(inst, Mapper.outMapper(mapper)); } public static <E, E2> Set<? super E2> mapIn(Set<? super E> inst, Function<? super E2, ? extends E> mapper) { return map(inst, Mapper.inMapper(mapper)); }}public class MappedCollection<E, E2> extends MappedIterable<E, E2> implements Collection<E2> { private final Collection<E> collection; private final Mapper<E, E2> mapper; public MappedCollection(Collection<E> collection, Mapper<E, E2> mapper) { super(collection, mapper); this.collection = collection; this.mapper = mapper; } public static <E, E2> Collection<E2> map(Collection<E> inst, Mapper<E, E2> mapper) { return new MappedCollection<>(inst, mapper); } public static <E, E2> Collection<? extends E2> mapOut(Collection<? extends E> inst, Function<? super E, ? extends E2> mapper) { return map(inst, Mapper.outMapper(mapper)); } public static <E, E2> Collection<? super E2> mapIn(Collection<? super E> inst, Function<? super E2, ? extends E> mapper) { return map(inst, Mapper.inMapper(mapper)); } @Override public int size() { return collection.size(); } @Override public boolean isEmpty() { return collection.isEmpty(); } @Override public boolean contains(Object o) { return collection.contains(mapper.uncheckedIn(o)); } @Override public @NotNull Object[] toArray() { return MappedArray.map(collection.toArray(), mapper.unchecked()); } @Override public @NotNull <T> T[] toArray(@NotNull T[] a) { return MappedArray.map(collection.toArray(), ContainersUtils.getComponentType(a), mapper.doubleUnchecked()); } @Contract(mutates = "this") @Override public boolean add(@NotNull E2 e2) { return collection.add(mapper.in(e2)); } @Contract(mutates = "this") @Override public boolean remove(Object o) { return collection.remove(mapper.uncheckedIn(o)); } @Override public boolean containsAll(@NotNull Collection<?> c) { return collection.containsAll(map(c, mapper.reverse().unchecked())); } @Contract(mutates = "this") @Override public boolean addAll(@NotNull Collection<? extends E2> c) { return collection.addAll(mapOut(c, mapper.reverse())); } @Contract(mutates = "this") @Override public boolean removeAll(@NotNull Collection<?> c) { return collection.removeAll(map(c, mapper.reverse().unchecked())); } @Contract(mutates = "this") @Override public boolean retainAll(@NotNull Collection<?> c) { return collection.retainAll(map(c, mapper.reverse().unchecked())); } @Contract(mutates = "this") @Override public void clear() { collection.clear(); }}@RequiredArgsConstructorpublic class MappedIterable<E, E2> implements Iterable<E2> { private final @NotNull Iterable<E> iterable; private final @NotNull Function<? super E, ? extends E2> mapper; public static <E, E2> Iterator<E2> map(Iterator<E> inst, Function<? super E, ? extends E2> mapper) { return new MappedIterator<>(inst, mapper); } @Override public @NotNull Iterator<E2> iterator() { return MappedIterator.map(iterable.iterator(), mapper); }}@RequiredArgsConstructorpublic class MappedIterator<E, E2> implements Iterator<E2> { private final @NotNull Iterator<E> iterator; private final @NotNull Function<? super E, ? extends E2> mapper; public static <E, E2> Iterator<E2> map(Iterator<E> inst, Function<? super E, ? extends E2> mapper) { return new MappedIterator<>(inst, mapper); } @Contract(pure = true) @Override public boolean hasNext() { return iterator.hasNext(); } @Override public E2 next() { return mapper.apply(iterator.next()); }}@UtilityClasspublic class MappedArray { public static <T> Object[] map(T[] inst, Function<? super T, ?> mapper) { return Stream.of(inst).map(mapper).toArray(); } public static <T, T2> T2[] map(T[] inst, Class<T2> clazz, Function<? super T, ? extends T2> mapper) { return Stream.of(inst).map(mapper).toArray(i -> ContainersUtils.createArray(clazz, i)); }}