Java中的不变集合

java提供了很多集合的接口和类,比如比较常用的List,它就有ArrayList,LinkedList等子类。这些类都是可变的(mutuable)。有时候,我们作为服务的提供方,不希望提供给客户端(此处的服务端和客户端是站在程序依赖的角度,而不是一般常说的基于C/S架构的服务端和客户端)的集合被对方改变内容。此时就需要利用原来内部可变的集合生成一个不可变的集合,提供给客户端。

在Java中,生成不可变集合主要是通过java.util包下的Collections工具类提供的方法。该类提供了很多生成不可变集合的方法,下面列举几个。

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c);

public static <T> Set<T> unmodifiableSet(Set<? extends T> s);

public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s);

public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s);

public static <T> List<T> unmodifiableList(List<? extends T> list);

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m);

通过方法签名可以看出,返回类型还是java.util包下常见的集合类型,只是他们的内容都是不可变的了。那么是怎么实现的呢?我们就以unmodifiableCollection这个类来举例说明吧。

    public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
        return new UnmodifiableCollection<>(c);
    }

这个方法很简单,就是返回了一个UnmodifiableCollection对象。

    static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 1820017752578914078L;

        final Collection<? extends E> c;

        UnmodifiableCollection(Collection<? extends E> c) {
            if (c==null)
                throw new NullPointerException();
            this.c = c;
        }

        public int size()                   {return c.size();}
        public boolean isEmpty()            {return c.isEmpty();}
        public boolean contains(Object o)   {return c.contains(o);}
        public Object[] toArray()           {return c.toArray();}
        public <T> T[] toArray(T[] a)       {return c.toArray(a);}
        public String toString()            {return c.toString();}

        public Iterator<E> iterator() {
            return new Iterator<E>() {
                private final Iterator<? extends E> i = c.iterator();

                public boolean hasNext() {return i.hasNext();}
                public E next()          {return i.next();}
                public void remove() {
                    throw new UnsupportedOperationException();
                }
                @Override
                public void forEachRemaining(Consumer<? super E> action) {
                    // Use backing collection version
                    i.forEachRemaining(action);
                }
            };
        }

        public boolean add(E e) {
            throw new UnsupportedOperationException();
        }
        public boolean remove(Object o) {
            throw new UnsupportedOperationException();
        }

        public boolean containsAll(Collection<?> coll) {
            return c.containsAll(coll);
        }
        public boolean addAll(Collection<? extends E> coll) {
            throw new UnsupportedOperationException();
        }
        public boolean removeAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }
        public boolean retainAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }
        public void clear() {
            throw new UnsupportedOperationException();
        }

        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> action) {
            c.forEach(action);
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            throw new UnsupportedOperationException();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Spliterator<E> spliterator() {
            return (Spliterator<E>)c.spliterator();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Stream<E> stream() {
            return (Stream<E>)c.stream();
        }
        @SuppressWarnings("unchecked")
        @Override
        public Stream<E> parallelStream() {
            return (Stream<E>)c.parallelStream();
        }
    }

通过这个类的定义我们可以看出,原来它的实现很简单。首先UnmodifiableCollection这个类采用了装饰器模式,它本身实现了Collection接口,同时在内部持有一个Collection对象。而构造方法就是把传入进来的集合对象赋值给自己持有的Collection对象。

接着,对于本来可以改变集合内容的方法,比如add,remove等方法,直接抛出UnsupportedOperationException异常。而对于不影响集合内容的方法,则直接委托给了持有的Collection对象。

其实这种不可变对象的使用的精髓在于,这个不可变对象并不是真的不可变的。而只是对于客户端是不可变的。因为客户端不能直接接触到内部持有的可变的集合。而当他通过这个不可变对象间接操作集合时,如果调用的是本来可能改变集合内容的方法,就被直接抛出异常了。

我们可以看下面这个简单例子:

public class UnmodifiedCollectionTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123", "456");
        Collection<String> collection = Collections.unmodifiableCollection(list);
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        list.set(0, "abc");
        iterator = collection.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

输出:

可见,虽然无法从外部改变集合实际的值。但是当内部集合改变的时候,从外部再次获取集合,内容也变了。可见,不可变也只是相对的不可变。

记得看《Java并发编程实战》这本书里,讲到线程安全的时候,说一个不可变对象是天然的线程安全的,不用考虑它的发布方式。而如果一个非线程安全的对象,则要将它采用安全的方式发布。

当要发布一个对象时,可以考虑把它包装成一个不可变对象发布出去,而把实际对象的可变性封装在内部。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值