    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 *      https://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 */
    017
    018package org.apache.commons.collections4.collection;
    019
    020import java.io.Serializable;
    021import java.util.Collection;
    022import java.util.Iterator;
    023import java.util.Objects;
    024import java.util.function.Consumer;
    025import java.util.function.Predicate;
    026
    027/**
    028 * Decorates another {@link Collection} to synchronize its behavior for a multithreaded environment.
    029 * <p>
    030 * Iterators must be manually synchronized:
    031 * </p>
    032 *
    033 * <pre>
    034 * synchronized (coll) {
    035 *     Iterator it = coll.iterator();
    036 *     // do stuff with iterator
    037 * }
    038 * </pre>
    039 * <p>
    040 * This class is Serializable from Commons Collections 3.1.
    041 * </p>
    042 *
    043 * @param <E> The type of the elements in the collection.
    044 * @since 3.0
    045 */
    046public class SynchronizedCollection<E> implements Collection<E>, Serializable {
    047
    048    /** Serialization version */
    049    private static final long serialVersionUID = 2412805092710877986L;
    050
    051    /**
    052     * Creates a synchronized collection.
    053     *
    054     * @param <T>  The type of the elements in the collection.
    055     * @param coll The collection to decorate, must not be null.
    056     * @return A new synchronized collection.
    057     * @throws NullPointerException if collection is null.
    058     * @since 4.0
    059     */
    060    public static <T> SynchronizedCollection<T> synchronizedCollection(final Collection<T> coll) {
    061        return new SynchronizedCollection<>(coll);
    062    }
    063
    064    /** The collection to decorate */
    065    private final Collection<E> collection;
    066
    067    /** The object to lock on, needed for List/SortedSet views */
    068    protected final Object lock;
    069
    070    /**
    071     * Constructs and wraps (not copies).
    072     *
    073     * @param collection The collection to decorate, must not be null.
    074     * @throws NullPointerException if the collection is null.
    075     */
    076    protected SynchronizedCollection(final Collection<E> collection) {
    077        this.collection = Objects.requireNonNull(collection, "collection");
    078        this.lock = this;
    079    }
    080
    081    /**
    082     * Constructs and wraps (not copies).
    083     *
    084     * @param collection The collection to decorate, must not be null.
    085     * @param lock       The lock object to use, must not be null.
    086     * @throws NullPointerException if the collection or lock is null.
    087     */
    088    protected SynchronizedCollection(final Collection<E> collection, final Object lock) {
    089        this.collection = Objects.requireNonNull(collection, "collection");
    090        this.lock = Objects.requireNonNull(lock, "lock");
    091    }
    092
    093    @Override
    094    public boolean add(final E object) {
    095        synchronized (lock) {
    096            return decorated().add(object);
    097        }
    098    }
    099
    100    @Override
    101    public boolean addAll(final Collection<? extends E> coll) {
    102        synchronized (lock) {
    103            return decorated().addAll(coll);
    104        }
    105    }
    106
    107    @Override
    108    public void clear() {
    109        synchronized (lock) {
    110            decorated().clear();
    111        }
    112    }
    113
    114    @Override
    115    public boolean contains(final Object object) {
    116        synchronized (lock) {
    117            return decorated().contains(object);
    118        }
    119    }
    120
    121    @Override
    122    public boolean containsAll(final Collection<?> coll) {
    123        synchronized (lock) {
    124            return decorated().containsAll(coll);
    125        }
    126    }
    127
    128    /**
    129     * Gets the collection being decorated.
    130     *
    131     * @return The decorated collection.
    132     */
    133    protected Collection<E> decorated() {
    134        return collection;
    135    }
    136
    137    @Override
    138    public boolean equals(final Object object) {
    139        synchronized (lock) {
    140            if (object == this) {
    141                return true;
    142            }
    143            return object == this || decorated().equals(object);
    144        }
    145    }
    146
    147    /**
    148     * @since 4.6.0
    149     */
    150    @Override
    151    public void forEach(final Consumer<? super E> action) {
    152        synchronized (lock) {
    153            decorated().forEach(action);
    154        }
    155    }
    156
    157    @Override
    158    public int hashCode() {
    159        synchronized (lock) {
    160            return decorated().hashCode();
    161        }
    162    }
    163
    164    @Override
    165    public boolean isEmpty() {
    166        synchronized (lock) {
    167            return decorated().isEmpty();
    168        }
    169    }
    170
    171    /**
    172     * Iterators must be manually synchronized.
    173     *
    174     * <pre>
    175     * synchronized (coll) {
    176     *     Iterator it = coll.iterator();
    177     *     // do stuff with iterator
    178     * }
    179     * </pre>
    180     *
    181     * @return An iterator that must be manually synchronized on the collection.
    182     */
    183    @Override
    184    public Iterator<E> iterator() {
    185        return decorated().iterator();
    186    }
    187
    188    @Override
    189    public boolean remove(final Object object) {
    190        synchronized (lock) {
    191            return decorated().remove(object);
    192        }
    193    }
    194
    195    @Override
    196    public boolean removeAll(final Collection<?> coll) {
    197        synchronized (lock) {
    198            return decorated().removeAll(coll);
    199        }
    200    }
    201
    202    /**
    203     * @since 4.4
    204     */
    205    @Override
    206    public boolean removeIf(final Predicate<? super E> filter) {
    207        synchronized (lock) {
    208            return decorated().removeIf(filter);
    209        }
    210    }
    211
    212    @Override
    213    public boolean retainAll(final Collection<?> coll) {
    214        synchronized (lock) {
    215            return decorated().retainAll(coll);
    216        }
    217    }
    218
    219    @Override
    220    public int size() {
    221        synchronized (lock) {
    222            return decorated().size();
    223        }
    224    }
    225
    226    @Override
    227    public Object[] toArray() {
    228        synchronized (lock) {
    229            return decorated().toArray();
    230        }
    231    }
    232
    233    @Override
    234    public <T> T[] toArray(final T[] object) {
    235        synchronized (lock) {
    236            return decorated().toArray(object);
    237        }
    238    }
    239
    240    @Override
    241    public String toString() {
    242        synchronized (lock) {
    243            return decorated().toString();
    244        }
    245    }
    246}