java 数据结构大全(Map、List、Set、BlockingQueue、BlockingDueue)

目录

1、集合框架

2、Iterable 接口

3、 Collection接口

3.1、 List接口

3.1.1、 ArrayList类、LinkedList类、Vector类

3.2、Set接口

3.2.1、HashSet类、TreeSet类、LinkedHashSet类

3.3、Map接口

3.3.1、HashMap类

3.3.2、 HashTable类

3.3.3、TreeMap类

3.3.4、 WeakHashMap类

3.4、Collections工具的一个功能:线程安全地(synchronized)使用非线程安全的集合

4、Concurrent包下的阻塞队列(都是线程安全的,因为使用了Lock)

4.1、ArrayBlockingQueue 类

4.2、LinkedBlockingQueue 类

4.3、SynchronousQueue 类

4.4、PriorityBlockingQueue 类

4.5、 DeleyQueue 类

4.5.1 讲讲队列中元素需要满足什么要求

4.5.2、 看看DelayQueue的源码(这个源码不难,耐心点看吧)

4.6、LinkedTransferQueue 类

5、并发性集合

5.1、ConcurrentHashMap类

5.2、ConcurrentSkipListMap 类

5.3、ConcurrentSkipListSet 类


1、集合框架

下面这个图基本包括了java中核心的集合结构,个人觉得还可以。

2、Iterable 接口

这个接口是集合系列的根接口,都要实现它,因为 Iterable 接口里面有个iterator()方法,用于遍历集合里的每一个元素,所以记着:凡是集合类,都可以通过iterator来遍历。

3、 Collection接口

下面是Collection接口的源码,定义了集合最基本的使用规范。不仅是java,其它的开源代码也是这样层层继承和实现,我们可能会比较疑惑,为什么要搞得这么复杂,把代码写在一起不好吗,干嘛弄出这么多的接口文件。层层分开的目的是让框架解耦,这样可以更加灵活地扩展和使用,你把代码都写到一堆,一旦需要更改或者扩展,会让你改得头疼。

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);
    // 添加一个元素,成功返回true,失败返回false
    boolean add(E e);
    // 删除一个元素,成功返回true,失败返回false
    boolean remove(Object o);
    // 集合是否包含该集合c(c是否是集合的子集)
    boolean containsAll(Collection<?> c);
    // 添加一个集合,成功返回true,失败返回false   
    boolean addAll(Collection<? extends E> c);
    // 删除一个集合,成功返回true,失败返回false
    boolean removeAll(Collection<?> c);
    // 删除满足指定条件的元素
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
    // 仅保留包含在指定集合c中的元素
    boolean retainAll(Collection<?> c);
    // 删除所有元素
    void clear();
    // 判等
    boolean equals(Object o);
    // 返回哈希码
    int hashCode();
    // 返回集合的Spliterator
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }
    // 返回以此集合作为源的Stream
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
    // 返回可能并行的以此集合作为源的Stream
    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

 上面是Collection接口的源码,重点说一下:removeIf()。

removeIf(Predicate<? super E> filter):删除满足指定条件的元素,那怎么去设置条件呢?下面举个例子,删除集合中大于24的元素,其实就是对集合的每个元素进行遍历,然后判断每个元素是否满足条件,如果满足,就删除。很多地方建议removeIf()里面用lambda表达式,确实,如果条件比较简单的话,用lambda表达式更简洁,但是对于复杂的条件,还是用java方式吧。

        ArrayList<Integer> collection = new ArrayList<>();
        collection.add(22);
        collection.add(40);
        collection.removeIf(new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer > 24;
            }
        });

3.1、 List接口

下面是List接口的源码,除去了从Collection接口继承的方法,只简单介绍了扩展的方法,熟悉这些方法的目的是让我们知道List集合框架下的结构可以有哪些属性和操作。

public interface List<E> extends Collection<E> {
    // 在指定位置处添加集合
    boolean addAll(int index, Collection<? extends E> c);
    // 将所有元素替换,具体的替换操作代码就写在参数位置即可
    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }
    // 给list排序,前提是元素能够比较(实现了Comparator接口)
    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }
    // 返回index处的元素 
    E get(int index);
    // 设置index处的元素为element
    E set(int index, E element);
    // 在index处添加元素element
    void add(int index, E element);
    // 删除index处的元素
    E remove(int index);
    // 返回指定元素的位置
    int indexOf(Object o);
    // 返回指定元素在list中最后一个的位置
    int lastIndexOf(Object o);
    // 迭代器
    ListIterator<E> listIterator();
    // 迭代器,从index处开始迭代
    ListIterator<E> listIterator(int index);
    // 截取list的一段出来
    List<E> subList(int fromIndex, int toIndex);
}

3.1.1、 ArrayList类、LinkedList类、Vector类

ArrayList类:线程不安全。底层是一个数组,new ArrayList(); 创建时默认为空数组(长度为0),当真正要用的时候,再扩展容量为10,其实也可以理解为ArrayList 的默认容量为10。之后的扩容机制:扩容后的大小= 原始大小*1.5。

优点:能够随机访问,相对于数组而言,能够扩容。

缺点:容易造成空间浪费,扩展容量的话,也是创建一个新的更大容量的数组,让后将旧数组的数据复制到新数组,麻烦;另外,不方便插入、删除操作。

LinkedList类:线程不安全。底层是一个双向链表。

优点:插入,删除操作非常方便。

缺点:遍历,查询不方便。

Vector类:线程安全。和ArrayList差不多,区别在于:Vector是线程安全的,因为用了synchronized关键字;Vector的扩容机制是扩展1倍,而ArrayList是扩展0.5倍。

3.2、Set接口

Set接口的源码没有什么可说的,因为都是继承自Collection接口的。

3.2.1、HashSet类、TreeSet类、LinkedHashSet类

HashSet类:线程不安全。底层是HashMap的,元素就是HashMap的key,value固定是PRESENT。此处暂不做详细介绍,看下面的HashMap即可。

TreeSet类:线程不安全。底层是TreeMap实现的。

LinkedHashSet类:线程不安全。既有hash功能,以时间复杂度O(1)读取,又有Linked 链表性质,保证元素的先后顺序,底层原理和HashSet差不多,只不过每个元素还有链表指针,维护着先后顺序。

都是用Map来实现的,那Set系列存在的意义呢?其实Set系列都用的少,替我们封装了一层,让我们用起来更方便;另外,Set里的元素必须不重复,这也是Set的优点。

3.3、Map接口

3.3.1、HashMap类

下面的图画得很好理解,所以引用一下,请原作者别喷。

线程不安全。HashMap是由数组+链表实现的,添加一个元素A时,首先要确定插入的位置:先计算其hashCode值,再根据hashCode计算出位置,如果该位置已经有元素了,那就比较两个元素是否是同一个,如果是,则替换,如果不是,即出现哈希冲突,将两个元素形成链表,然后表头插入插入数组的该位置处,新来的元素A放入表头,就形成了链表。当链表的长度超过8时,就自动将链表转为红黑树结构。

默认情况下,数组的初始长度为16。默认的负载因子是0.75。当元素的个数超过 数组长度 * 负载因子 时,就扩容,数组长度翻倍。

负载因子:比如一个哈希数组长度为16时,里面存的元素越多,那么下一次存入元素时发生哈希冲突的可能性就越大,如果数组都满了,再存入一个元素,那哈希冲突概率就是100%。一旦冲突了,就会用链表的方法去解决,你要知道哈希的查询是O(1),但是链表的查询是要从表头挨个挨个地去找的,所以链表的大量存在会影响HashMap的性能。因此,不能等到数组都存满了再扩容,所以设置了一个负载因子,负载因子表明一个数组最多存储的元素比例,比如,因子 = 0.75时,长度为16的HashMap最多存入12个元素,然后就得扩容为32。

扩容:扩容比较麻烦,大小从16变为了32,之前元素的哈希位置需要重新计算;另外还得重新创建数组,将元素复制过去。因此,如果能提前预料到HashMap的最大容量是最好的,就不会出现扩容了。

负载因子的选择需要选好,大了,哈希冲突多,链表多,影响性能;小了,浪费空间。

如果要自己设置hashMap 的大小,最好设置为2的幂,为了让哈希计算出来的索引位置均匀地分布在数组里。如果不是2的幂,那么在二进制取模运算时,有些index结果的出现几率会更大,而有些index可能永远不会出现。

顺便说一下HashMap的哈希算法,hashCode方法是将对象的地址进行处理,高16位与低16位进行异或,再将异或的结果与(数组的长度 - 1)做 & 运算,得出的结果就是对象应该插入在HashMap 数组中的位置。

常用方法:

// 添加键值对
public V put(K key, V value);
// 添加很多键值对
public void putAll(Map<? extends K, ? extends V> m);
// 获取key对于的值
public V get(Object key);
// 是否包含该key
public boolean containsKey(Object key);
// 是否包含该value
public boolean containsValue(Object value);
// 删除key对应的键值对
public V remove(Object key);
// 获取所有value的集合
public Collection<V> values();
// 判断是否为空
public boolean isEmpty();
// 返回一个由所有的key组成的一个Set
public Set<K> keySet();
// 返回一个由所有键值对组成的一个Set
public Set<Map.Entry<K,V>> entrySet();
// 清空
public void clear();
// 获取大小
public int size();

3.3.2、 HashTable类

线程安全。虽然HashTable已经被弃用了,还是介绍一下吧,HashTable的结构与HashMap的一样,两者的主要区别在于:

1、HashTable是线程安全的(sychronized);

2、HashTable不允许key,value为null;

3、HashTable的默认大小为11,扩容时,变为原来大小的2倍+1。

HashTable被弃用了,肯定是有新的类替代它,下文会介绍ConcurrentHashMap。

3.3.3、TreeMap类

线程不安全。TreeMap类的底层结构是一颗红黑树,即自平衡二叉排序树,因此,查询操作的时间复杂度为O(log n)。如果说仅仅查询,添加,删除操作,那TreeMap的性能是不如HashMap的,但是TreeMap是能够自动排序的,这也是TreeMap存在的意义。

每添加/删除一个元素时,TreeMap都会自动调整自己的结构以再次让自己成为平衡二叉排序树,当我们要使用TreeMap的自动排序特性时,需要将所有元素/所有key导出为一个集合,然后这个集合里的内容是有序的(默认按照key升序),当然我们可以指定排序的规则,使得获得的集合按照我们自定义的规则来排序,因此,就需要让key实现Comparable接口。

常用方法:

1、public TreeMap():默认的构造方法,按key自然排序。

2、public TreeMap(Map<? extends K, ? extends V> m):将Map里的键值对直接构造成红黑树,key按照自然排序。

3、public TreeMap(Comparator<? super K> comparator):指定比较器,key按照比较器排序。

4、public V get(Object key):根据key获取value。

5、public V put(K key, V value):添加key-value。

6、Map.Entry<K,V> ceilingEntry(K key):返回大于等于此key 的元素。

7、K ceilingKey(K key):返回大于等于此key的key。

8、void clear():清空。

9、Object clone():返回此 TreeMap实例的浅拷贝。

10、Comparator<? super K> comparator():返回比较器,如果是自然排序,即返回null。

11、boolean containsKey(Object key):是否包含指定key。

12、boolean containsValue(Object value):是否包含指定value。

13、NavigableSet<K> descendingKeySet():以反序的方式返回所有的key

14、NavigableMap<K,V> descendingMap():以反序的方式返回所有键值对。

15、Set<Map.Entry<K,V>> entrySet():返回包含所有键值对的集合。

16、Map.Entry<K,V> firstEntry():返回最小键相关联的键值对 。

17、K firstKey():返回第一个(最低)键。

18、Map.Entry<K,V> floorEntry(K key):返回小于等于此key的键值对。

19、K floorKey(K key):返回小于等于此key 的key。

20、Set<K> keySet():返回包含所有key的集合。

21、Map.Entry<K,V> lastEntry():返回最大key对应的键值对。

22、K lastKey():返回最大key。

23、void putAll(Map<? extends K,? extends V> map):将map添加进TreeMap中。

24、V remove(Object key):删除key。

25、int size():返回键值对的总数量。

26、Collection<V> values():返回包含所有值的集合。

3.3.4、 WeakHashMap类

这是一个和HashMap类似的哈希结构类,常用的操作方法就是map那套,因为平时也没有用过,所以此处暂不作详细介绍,只是这个类是一个弱引用的哈希映射,在垃圾回收时,不管当前内存是否够用,都会回收掉弱引用指向的内存空间的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值