001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.set;
018
019import java.io.Serializable;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.Map;
023import java.util.Objects;
024import java.util.Set;
025import java.util.function.Predicate;
026
027/**
028 * Decorates a {@code Map} to obtain {@code Set} behavior.
029 * <p>
030 * This class is used to create a {@code Set} with the same properties as
031 * the key set of any map. Thus, a ReferenceSet can be created by wrapping a
032 * {@code ReferenceMap} in an instance of this class.
033 * </p>
034 * <p>
035 * Most map implementation can be used to create a set by passing in dummy values.
036 * Exceptions include {@code BidiMap} implementations, as they require unique values.
037 * </p>
038 *
039 * @param <E> the type of the elements in this set
040 * @param <V> the dummy value type in this map
041 * @since 3.1
042 */
043public final class MapBackedSet<E, V> implements Set<E>, Serializable {
044
045    /** Serialization version */
046    private static final long serialVersionUID = 6723912213766056587L;
047
048    /**
049     * Factory method to create a set from a map.
050     *
051     * @param <E> the element type
052     * @param <V> the dummy value type in the map
053     * @param map  the map to decorate, must not be null
054     * @return a new map backed set
055     * @throws NullPointerException if map is null
056     * @since 4.0
057     */
058    public static <E, V> MapBackedSet<E, V> mapBackedSet(final Map<E, ? super V> map) {
059        return mapBackedSet(map, null);
060    }
061
062    /**
063     * Factory method to create a set from a map.
064     *
065     * @param <E> the element type
066     * @param <V> the dummy value type in the map
067     * @param map  the map to decorate, must not be null
068     * @param dummyValue  the dummy value to use
069     * @return a new map backed set
070     * @throws NullPointerException if map is null
071     * @since 4.0
072     */
073    public static <E, V> MapBackedSet<E, V> mapBackedSet(final Map<E, ? super V> map, final V dummyValue) {
074        return new MapBackedSet<>(map, dummyValue);
075    }
076
077    /** The map being used as the backing store */
078    private final Map<E, ? super V> map;
079
080    /** The dummyValue to use */
081    private final V dummyValue;
082
083    /**
084     * Constructor that wraps (not copies).
085     *
086     * @param map  the map to decorate, must not be null
087     * @param dummyValue  the dummy value to use
088     * @throws NullPointerException if map is null
089     */
090    private MapBackedSet(final Map<E, ? super V> map, final V dummyValue) {
091        this.map = Objects.requireNonNull(map, "map");
092        this.dummyValue = dummyValue;
093    }
094
095    @Override
096    public boolean add(final E obj) {
097        final int size = map.size();
098        map.put(obj, dummyValue);
099        return map.size() != size;
100    }
101
102    @Override
103    public boolean addAll(final Collection<? extends E> coll) {
104        final int size = map.size();
105        for (final E e : coll) {
106            map.put(e, dummyValue);
107        }
108        return map.size() != size;
109    }
110
111    @Override
112    public void clear() {
113        map.clear();
114    }
115
116    @Override
117    public boolean contains(final Object obj) {
118        return map.containsKey(obj);
119    }
120
121    @Override
122    public boolean containsAll(final Collection<?> coll) {
123        return map.keySet().containsAll(coll);
124    }
125
126    @Override
127    public boolean equals(final Object obj) {
128        return map.keySet().equals(obj);
129    }
130
131    @Override
132    public int hashCode() {
133        return map.keySet().hashCode();
134    }
135
136    @Override
137    public boolean isEmpty() {
138        return map.isEmpty();
139    }
140
141    @Override
142    public Iterator<E> iterator() {
143        return map.keySet().iterator();
144    }
145
146    @Override
147    public boolean remove(final Object obj) {
148        final int size = map.size();
149        map.remove(obj);
150        return map.size() != size;
151    }
152
153    @Override
154    public boolean removeAll(final Collection<?> coll) {
155        return map.keySet().removeAll(coll);
156    }
157
158    /**
159     * @since 4.4
160     */
161    @Override
162    public boolean removeIf(final Predicate<? super E> filter) {
163        return map.keySet().removeIf(filter);
164    }
165
166    @Override
167    public boolean retainAll(final Collection<?> coll) {
168        return map.keySet().retainAll(coll);
169    }
170
171    @Override
172    public int size() {
173        return map.size();
174    }
175
176    @Override
177    public Object[] toArray() {
178        return map.keySet().toArray();
179    }
180
181    @Override
182    public <T> T[] toArray(final T[] array) {
183        return map.keySet().toArray(array);
184    }
185
186}