/* * JBoss, Home of Professional Open Source * Copyright 2014, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.weld.util.collections; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import org.jboss.weld.util.Preconditions; /** * Weld's immutable set implementation. Instances returned from methods of this class may use different strategies to achieve good performance / memory * consumption balance. * <p> * These strategies include: * <ul> * <li>A single shared {@link Set} implementation instance representing an empty list</li> * <li>An optimized implementation for holding one, two or three references.</li> * <li>An immutable {@link Set} implementation based on hashing</li> * </ul> * <p/> * * @author Jozef Hartinger * @ee WELD-1753 * * @param <T> the type of elements */ public abstract class ImmutableSet<T> extends AbstractImmutableSet<T> { ImmutableSet() { } /** * Creates a new immutable set that consists of the elements in the given collection. If the given collection is already an instance created by * {@link ImmutableSet}, the instance is re-used. * * @param collection the given collection * @return a new immutable set that consists of the elements in the given collection */ @SuppressWarnings("unchecked") public static <T> Set<T> copyOf(Collection<? extends T> collection) { Preconditions.checkNotNull(collection); if (collection instanceof AbstractImmutableSet<?>) { return (Set<T>) collection; } if (collection.isEmpty()) { return Collections.emptySet(); } if (collection instanceof Set) { return from((Set<T>) collection); } return ImmutableSet.<T> builder().addAll(collection).build(); } /** * Creates a new immutable set that consists of given elements. * * @param elements the given elements * @return a new immutable set that consists of given elements */ @SafeVarargs public static <T> Set<T> of(T... elements) { Preconditions.checkNotNull(elements); return ImmutableSet.<T> builder().addAll(elements).build(); } /** * Returns a collector that can be used to collect items of a stream into an immutable set. * * @return collector */ @SuppressWarnings("unchecked") public static <T> ImmutableSetCollector<T> collector() { return (ImmutableSetCollector<T>) ImmutableSetCollector.INSTANCE; } /** * Creates a new empty builder for building immutable sets. * * @return a new empty builder */ public static <T> Builder<T> builder() { return new BuilderImpl<T>(); } /** * Builder for building immutable sets. The builder may be re-used after build() is called. * * @author Jozef Hartinger * * @param <T> the type of elements */ public interface Builder<T> { Builder<T> add(T item); Builder<T> addAll(Iterable<? extends T> items); Builder<T> addAll(@SuppressWarnings("unchecked") T... items); /** * Create a new immutable set. * * @return a new immutable set */ Set<T> build(); } private static class BuilderImpl<T> implements Builder<T> { private Set<T> set; private BuilderImpl() { this.set = new LinkedHashSet<>(); } @Override public Builder<T> add(T item) { if (item == null) { throw new IllegalArgumentException("This collection does not support null values"); } set.add(item); return this; } @Override public Builder<T> addAll(@SuppressWarnings("unchecked") T... items) { for (T item : items) { add(item); } return this; } @Override public Builder<T> addAll(Iterable<? extends T> items) { for (T item : items) { add(item); } return this; } BuilderImpl<T> addAll(BuilderImpl<T> items) { addAll(items.set); return this; } @Override public Set<T> build() { return from(set); } } private static <T> Set<T> from(Set<T> set) { switch (set.size()) { case 0: return Collections.emptySet(); case 1: return new ImmutableTinySet.Singleton<T>(set); case 2: return new ImmutableTinySet.Doubleton<T>(set); case 3: return new ImmutableTinySet.Tripleton<T>(set); default: return new ImmutableHashSet<>(set); } } private static class ImmutableSetCollector<T> implements Collector<T, BuilderImpl<T>, Set<T>> { private static final ImmutableSetCollector<Object> INSTANCE = new ImmutableSetCollector<>(); private static final Set<Characteristics> CHARACTERISTICS = of(Characteristics.UNORDERED); @Override public Supplier<BuilderImpl<T>> supplier() { return BuilderImpl::new; } @Override public BiConsumer<BuilderImpl<T>, T> accumulator() { return BuilderImpl::add; } @Override public BinaryOperator<BuilderImpl<T>> combiner() { return (builder1, builder2) -> builder1.addAll(builder2); } @Override public Function<BuilderImpl<T>, Set<T>> finisher() { return BuilderImpl::build; } @Override public Set<java.util.stream.Collector.Characteristics> characteristics() { return CHARACTERISTICS; } } }