• 技术文章 >Java >java教程

    详细介绍Java容器相关知识全面总结(图)

    黄舟黄舟2017-03-22 11:04:46原创701
    Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题。因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAdapter+ArrayList三剑客, 平时接触使用的容器也只有ArrayList和HashMap。导致对于整个Java容器体系的掌握和使用还停留在很浅的层面。省不足而思改进,那么跟着我来总结一下Java容器的相关知识吧。

    结构

    java容器类的继承结构

    Java容器类库定义了两个不同概念的容器,Collection和Map

    (文中Jdk源码版本无特殊说明均为jdk1.8.0_101)

        public interface Collection<E> extends Iterable<E> {
            int size();
    
            boolean isEmpty();
    
            boolean contains(Object o);
    
            Iterator<E> iterator();
    
            Object[] toArray();
    
            <T> T[] toArray(T[] a);
    
            boolean add(E e);
    
            boolean remove(Object o);
    
            boolean containsAll(java.util.Collection<?> c);
    
            boolean addAll(java.util.Collection<? extends E> c);
    
            boolean removeAll(java.util.Collection<?> c);
    
            ... //省略了其他方法
        }

    可以看到,java定义了Collection接口和内部集合的基本操作方法,Collection默认可以进行对集合末端添加元素,删除指定元素等操作。List、Set、Queue接口都继承自Collection并定义了各自不同的方法。

        public interface Map<K,V> {
            int size();
    
            boolean containsKey(Object key);
    
            boolean containsValue(Object value);
    
            V get(Object key);
    
            V put(K key, V value);
    
            V remove(Object key);
    
            void putAll(java.util.Map<? extends K, ? extends V> m);
    
            void clear();
    
            Set<K> keySet();
    
            Collection<V> values();
    
            Set<java.util.Map.Entry<K, V>> entrySet();
    
            interface Entry<K,V> {
                K getKey();
    
                V getValue();
    
                V setValue(V value);
    
                boolean equals(Object o);
    
                int hashCode();
    
                ...
            }
    
            boolean equals(Object o);
    
            int hashCode();
    
        }

    Map内部接口Entry<K,V>对应着Map的键值对。

    具体介绍

    迭代器

    先介绍一下迭代器。迭代器本身也是一种设计模式,设计的初衷在于:容器的实现由很多种,而我们想对容器进行遍历操作的话,首先不应该关心容器实现的细节,其次遍历操作应该是轻量级的。迭代器统一了对容器的访问方式,同时创建它的代价很小。值得注意的是,Iterator只能单向移动。

        public interface Iterator<E> {
            boolean hasNext();
    
            E next();
    
            default void remove() {
                throw new UnsupportedOperationException("remove");
            }
    
            default void forEachRemaining(Consumer<? super E> action) {
                Objects.requireNonNull(action);
                while (hasNext())
                    action.accept(next());
                }
        }

    通过容器的iterator()方法拿到容器的迭代器
    迭代器的next()获取下一个元素
    hasNext()判断是否还有元素
    remove()删除指定元素

    ListIterator

    ListIterator是Iterator的扩展之内,用于各种List类访问,支持双向移动。

    Collection

    List

    List 承诺可以将元素维护在特定的序列中.List接口在Collection的基础上添加了大量的方法,使得可以再List中间插入和移除元素。

        public interface List<E> extends Collection<E> {
    
            ...
    
            boolean add(E e);
    
            boolean remove(Object o);
    
            boolean containsAll(Collection<?> c);
    
            boolean addAll(Collection<? extends E> c);
    
            boolean addAll(int index, Collection<? extends E> c);
    
            boolean removeAll(Collection<?> c);
    
            boolean retainAll(Collection<?> c);
    
            E get(int index);
    
            E set(int index, E element);
    
            void add(int index, E element);
    
            E remove(int index);
    
            int indexOf(Object o);
    
            int lastIndexOf(Object o);
    
            java.util.List<E> subList(int fromIndex, int toIndex);
    
            ...
    
        }

    有两种类型的List,ArrayList和LinkedList

    List类型优点缺点底层实现
    ArrayList随机访问元素较快中间元素的插入和删除较慢数组
    LinkedList中间元素的插入和删除,顺序访问的优化随机访问元素较慢双向链表

    Set

    Set不保存重复的元素,通常用于快速查找元素。值得一提的是,Set具有与Collection完全一样的接口,没有任何额外的功能。 存入的元素必须定义equals()方法

    Set类型使用场景底层实现
    HashSet快速查找,元素必须定义hashCode()链表
    TreeSet保持次序,元素必须实现Comparable接口红-黑树结构
    LinkedHashSet维护次序的HashSet, 元素必须定义hashCode()链表

    Queue

    除了并发应用,Queue仅有的两个实现是LinkedList和PriorityQueue, 其中LinkedList同时实现了List, Deque接口。它们的差异在于排序行为而不是性能。

        public interface Queue<E> extends Collection<E> {
            boolean add(E e);
    
            boolean offer(E e);
    
            E remove();
    
            E poll();
    
            E element();
    
            E peek();
        }

    Map

    Map类型使用场景底层实现
    HashMap快速查询散列表
    LinkedHashMap迭代遍历具有顺序(插入顺序 or 最近最少使用)链表
    TreeMap具有排序,唯一可以返回子树的Map(subMap())红-黑树结构
    WeakHashMap弱键映射,映射之外无引用的键,可以被垃圾回收散列表
    ConcurrentHashMap线程安全的Map链表
    IdentityHashMap使用==代替equals()对键进行排序,专位解决特殊问题链表

    我们可以手工调整HashMap来调整性能,涉及到如容量、初始容量、尺寸、负载因子等概念。感兴趣的话可以看一些相关资料。

    一些建议

    进阶·并发容器

    这里不会讨论的太细致的实现,仅仅简单介绍一下基础知识,感兴趣的可以阅读《Java 并发编程的艺术》这本书。

    CopyOnWriteArrayList与Copy-On-Write策略

    Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

    CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

    CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。

    ConcurrentLinkedQueue

    在并发编程中,有时候需要使用线程安全的队列或列表。通常实现线程安全有两种方式,一种是使用阻塞算法,一种是使用非阻塞算法。非阻塞算法实现基础为循环CAS(Compare and Swipe 比较和交换)。

    ConcurrentLinkedQueue技术上的实现与CopyOnWriteArrayList与Copy类似,但是容器只有部分内容而不是整个容器可以被复制和修改。ConcurrentLinkedQueue有head节点和tail节点组成,每个节点由节点元素(item)和指向下一个结点(next)的引用组成。节点之间通过next关联起来,形成一张链表结构的队列。

    ConcurrentHashMap与锁分段技术

    ConcurrentHashMap是线程安全且高效的HashMap。多线程环境下,使用非线程安全的HashMap会导致死循环,而如文章中建议的那样,HashTable这种过时容器效率低下(使用synchronized来保证线程安全)。ConcurrentHashMap使用锁分段技术,大大提高了并发使用的效率。

    锁分段技术: 假设容器有多把锁,每一把锁用于锁容器其中一部分数据,当多线程访问容器不同数据段数据时,线程间就不存在锁竞争,从而提高并发访问效率。

    阻塞队列

    JDK7 提供了7个阻塞队列,实现原理都是基于生产-消费模式的等待通知机制。

    阻塞队列类型特点
    ArrayBlockingQueue由数组结构组成的有界阻塞队列
    LinkedBlockingQueue由链表结构组成的有界阻塞队列
    PriorityBlockingQueue支持优先级排序的无界阻塞队列
    DelayQueue使用优先级队列实现的无界阻塞队列
    SynchronousQueue不储存元素的阻塞队列
    LinkedTransferQueue由链表结构组成的无界阻塞队列
    LinkedBlockingQueue由链表结构组成的双向阻塞队列

    以上就是详细介绍Java容器相关知识全面总结(图)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
    专题推荐:Java,容器
    上一篇:详解Java开源的11个中文分词器使用方法和分词效果对比 下一篇:详解Java中可重入锁ReentrantLock原理的示例代码
    线上培训班

    相关文章推荐

    • 理解java8中java.util.function.*pojo反射新方法(附代码)• 浅析安卓app和微信授权登录及分享完整对接(代码分享)• 一招教你使用java快速创建Map(代码分享)• 教你一招搞定时序数据库在Spring Boot中的使用• 一文讲解Java中初始化List集合的8种方式(附代码)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网