Java 集合框架_开篇

作者: wo883721 | 来源:发表于2017-12-20 20:45 被阅读515次

    Java 集合框架系列

    Java 集合框架_开篇
    Java 集合框架_List
    Java 集合框架_ArrayList(源码解析)
    Java 集合框架_LinkedList(源码解析)
    Java 集合框架_Map
    Java 集合框架_AbstractMap
    数据结构_哈希表(Java)
    Java 集合框架_HashMap中的算法
    数据结构_二叉搜索树(Java)详解

    Java有三大工具类框架非常值得我们学习,java集合框架,java并发框架,以及java io流框架。
    Java 集合框架从顶层接口来说分成两类:

    1. 一个是列表集合,只包含value。 Collection接口以及它的子类。
    2. 一个是键值对形式的映射表集合,包含key->value。Map接口以及它的子类。
      这一章我们先从Collection接口说起。

    一.Collection 接口

    public interface Collection<E> extends Iterable<E> {}
    

    它继承了Iterable接口,那么这个接口有什么用呢?

    1. Iterable 接口

    /**
     * 可迭代接口,用来返回 顺序遍历迭代器Iterator和并行遍历迭代器Spliterator。
     * @param <T>
     */
    public interface Iterable<T> {
        Iterator<T> iterator();
    
        // 提供默认实现, 遍历集合每个元素,使它们都调用action.accept方法。
        // 因为Iterable接口有iterator方法提供迭代器,所以可以使用高级for循环
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    
        // splitable iterator可分割迭代器, 为了并行遍历数据源中的元素而设计的迭代器
        default Spliterator<T> spliterator() {
            return Spliterators.spliteratorUnknownSize(iterator(), 0);
        }
    }
    

    Iterable接口表示可迭代接口,它会返回一个迭代器Iterator。通过这个迭代器,我们可以遍历集合中元素了。
    它接口默认实现的方式,提供一个forEach方法,方便子类遍历集合中元素。

     // 不用for循环的方式了。
      list.forEach(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.print(" integer=="+integer);
                }
            });
    

    Spliterator<T> 表示并行遍历迭代器,是jdk1.8增加的新方法。

    2. 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()方法来判断。例如:

       public static void main(String[] args) {
            List<Integer> list = createList();
            Iterator iterator = list.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    

    迭代器还有一个remove方法,表示我们可以删除集合中当前遍历到的元素,也就是说迭代器不但可以遍历集合,还可以修改集合。

    但是这里为什么提供一个默认实现呢,因为有的集合本身就是不可修改集合,它就强制复写remove方法,而调用remove方法应该抛出异常。所以这里用了jdk1.8新特性,接口中直接提供这个默认实现。

    再回到Collection接口,我们想一下,作为一个集合顶层接口,它应该包含哪些方法呢?想到那个对数据数据操作最经典的话,增删改查。

    1. 添加元素的方法:
      boolean add(E e);
      boolean addAll(Collection<? extends E> c);
    
    1. 删除元素的方法:
       boolean remove(Object o);
       boolean removeAll(Collection<?> c);
       boolean retainAll(Collection<?> c);
       void clear(); 
    
    1. 改这个操作在Collection接口没有,因为它没有提供索引,没办法更新对应索引的值。
    2. 查这个操作在Collection接口,集合中是否包含某些元素,以及返回迭代器遍历集合
      boolean contains(Object o);
      boolean containsAll(Collection<?> c);
      Iterator<E> iterator(); 
    
    1. 其他重要方法:
      int size(); 
      boolean isEmpty();
      Object[] toArray();
      <T> T[] toArray(T[] a);
    
    public interface Collection<E> extends Iterable<E> {
    
        // 添加元素
        boolean add(E e);
    
        boolean addAll(Collection<? extends E> c);
    
        // 删除元素
        boolean remove(Object o);
    
        boolean removeAll(Collection<?> c);
        
        void clear();
    
        // 只保留c集合中包含的元素
        boolean retainAll(Collection<?> c);
    
    
        // 是否包含某些元素
        boolean contains(Object o);
    
        boolean containsAll(Collection<?> c);
    
        // 遍历集合
        Iterator<E> iterator();
    
        // 其他重要方法
        int size();
    
        boolean isEmpty();
    
        Object[] toArray();
    
    
        <T> T[] toArray(T[] a);
    
        // 根据filter返回值,删除集合中的元素
        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;
        }
    
        boolean equals(Object o);
    
        int hashCode();
    
    
        // 将一个集合转换成Stream,这样可以对集合中的元素进行过滤去重等处理。
        @Override
        default Spliterator<E> spliterator() {
            return Spliterators.spliterator(this, 0);
        }
    
        default Stream<E> stream() {
            return StreamSupport.stream(spliterator(), false);
        }
    
        default Stream<E> parallelStream() {
            return StreamSupport.stream(spliterator(), true);
        }
    }
    

    二. AbstractCollection 抽样类

    AbstractCollection抽样类实现了Collection接口大部分方法,只剩下iterator()和size()方法需要子类复写,也就是说需要子类提供集合元素。

    1. 添加元素

        public boolean add(E e) {
            throw new UnsupportedOperationException();
        }
    
        public boolean addAll(Collection<? extends E> c) {
            boolean modified = false;
            for (E e : c)
                if (add(e))
                    modified = true;
            return modified;
        }
    

    add(E e) 方法直接抛出异常,表示AbstractCollection默认是不可修改的集合,子类必须复写这个方法,才能向集合中添加元素。
    addAll(Collection<? extends E> c) ,通过for循环,将c集合中每个元素都添加到本集合中。

    高级for循环这种格式,默认就是使用迭代器实现的,也就是说只要是Iterable 接口子类,都可以使用高级for循环。

    2. 删除元素

      public boolean remove(Object o) {
            Iterator<E> it = iterator();
            // 注意这里,集合中有很多地方都是以o是否为空,分成两部分计算,虽然代码逻辑几乎一样。
            if (o==null) {
                while (it.hasNext()) {
                    if (it.next()==null) {
                        it.remove();
                        return true;
                    }
                }
            } else {
                while (it.hasNext()) {
                    if (o.equals(it.next())) {
                        it.remove();
                        return true;
                    }
                }
            }
            return false;
        }
    

    删除单个元素,通过迭代器iterator,遍历集合中元素,如果与o相等,就通过迭代器的remove方法,将它删除。

    注意这里会根据被删除元素o是否为空,分成两部分,虽然两部分代码逻辑几乎一模一样。如果设计成在循环中进行两个条件判断,这样也可以,但是会增加比较次数。

     public boolean removeAll(Collection<?> c) {
            Objects.requireNonNull(c);
            boolean modified = false;
            Iterator<?> it = iterator();
            while (it.hasNext()) {
                if (c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
    

    通过迭代器遍历本集合中每个元素,如果c集合中也包含这个元素,那么就删除这个元素。

    public boolean retainAll(Collection<?> c) {
            Objects.requireNonNull(c);
            boolean modified = false;
            Iterator<E> it = iterator();
            while (it.hasNext()) {
                if (!c.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
    

    这个方法和removeAll方法正好相反,遍历本集合中每个元素,如果c集合中不包含这个元素,那么就删除这个元素。

      public void clear() {
            Iterator<E> it = iterator();
            while (it.hasNext()) {
                it.next();
                it.remove();
            }
        }
    

    删除集合中全部元素。遍历集合中每个元素,得到一个删除一个,最终删除全部元素。

    3. 查找元素

       public boolean contains(Object o) {
            Iterator<E> it = iterator();
            if (o==null) {
                while (it.hasNext())
                    if (it.next()==null)
                        return true;
            } else {
                while (it.hasNext())
                    if (o.equals(it.next()))
                        return true;
            }
            return false;
        }
    

    遍历每个元素,如果有和o对象相等的,就直接返回true。如果遍历全部元素,还是没有找到相等元素,就返回false。

     public boolean containsAll(Collection<?> c) {
            for (Object e : c)
                if (!contains(e))
                    return false;
            return true;
        }
    

    是否包含c集合中全部元素。

    4. 其他重要方法

      public abstract Iterator<E> iterator();
        public abstract int size();
    

    这两个方法等待子类复写。

    public boolean isEmpty() {
            return size() == 0;
        }
    

    通过size()方法判断集合是否为空。

    public Object[] toArray() {
            // 将terator迭代器中的元素全部转换成Object[],我们必须知道集合元素的大小。
            // 但是size()返回值并不是集合元素的数量,所以要分情况
            Object[] r = new Object[size()];
            Iterator<E> it = iterator();
            for (int i = 0; i < r.length; i++) {
                // 迭代器已经遍历完成,说明集合元素数量就是i。用Arrays.copyOf(r, i)就可以返回i长度的Object数组了
                if (! it.hasNext())
                    return Arrays.copyOf(r, i);
                r[i] = it.next();
            }
            // 如果it.hasNext()为true,表示集合中还有元素,那么就要扩大r数组,以便将剩余元素都存放进去。
            // 如果为false,那么表示集合数量就是size(),直接返回这个r数组。
            return it.hasNext() ? finishToArray(r, it) : r;
        }
    

    将集合转成Object[]数组,这个方法很重要。要转成数组,那么必须知道数组的大小。

    这里需要注意的是size()返回值有可能和集合中元素数量不一样,因为强制约束它们两个值是相同的,要考虑到这种情况。
    所以这里我们先创建一个size()长度的Object[],然后遍历迭代器,将值存入数组对应下标位置。然后就有三种情况:

    1. 迭代器先遍历完成,就说明集合元素数量小于size()大小,而集合元素数量就是这个i, 然后通过Arrays.copyOf(r, i)方法,返回i长度的数组。
    2. 迭代器和r数组一起遍历完成,那么集合元素长度就是size(),直接返回这个r数组。
    3. 迭代器还没有遍历完成,那么就要对r数组进行扩容,存放剩余的集合元素,通过finishToArray(r, it)方法实现。
    public <T> T[] toArray(T[] a) {
            // Estimate size of array; be prepared to see more or fewer elements
            int size = size();
            T[] r = a.length >= size ? a :
                      (T[])java.lang.reflect.Array
                      .newInstance(a.getClass().getComponentType(), size);
            Iterator<E> it = iterator();
    
            for (int i = 0; i < r.length; i++) {
                if (! it.hasNext()) { // fewer elements than expected
                    if (a == r) {
                        r[i] = null; // null-terminate
                    } else if (a.length < i) {
                        return Arrays.copyOf(r, i);
                    } else {
                        System.arraycopy(r, 0, a, 0, i);
                        if (a.length > i) {
                            a[i] = null;
                        }
                    }
                    return a;
                }
                r[i] = (T)it.next();
            }
            // more elements than expected
            return it.hasNext() ? finishToArray(r, it) : r;
        }
    
     private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
            int i = r.length;
            while (it.hasNext()) {
                int cap = r.length;
                // 如果i == cap为true,表示数组已经满了,需要扩充。
                if (i == cap) {
                    // 将数组长度扩充0.5倍。 (cap >> 1) 相当于 cap / 2
                    int newCap = cap + (cap >> 1) + 1;
                    // 防止数组长度超出Integer最大值
                    if (newCap - MAX_ARRAY_SIZE > 0)
                        newCap = hugeCapacity(cap + 1);
                    // 将原数组元素新数组中,并返回新数组。
                    r = Arrays.copyOf(r, newCap);
                }
                r[i++] = (T)it.next();
            }
            // trim if overallocated
            return (i == r.length) ? r : Arrays.copyOf(r, i);
        }
    
      private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError
                    ("Required array size too large");
            return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
        }
    

    总结

    Collection 接口

    1. Collection 接口继承自Iterable接口,Iterable接口表示一个可迭代接口,主要作用就是返回一个迭代器Iterator。
    2. 顶层迭代器Iterator有三个重要方法:
    • boolean hasNext();
    • E next();
    • void remove()。
      用来遍历和移除集合中的元素。
    1. Collection 接口中重要方法:
    • boolean add(E e); 向集合中添加一个元素
    • boolean addAll(Collection<? extends E> c); 将集合c中元素都添加到集合中。
    • boolean remove(Object o); 从集合中删除o元素,也有可能删除不成功
    • boolean removeAll(Collection<?> c); 从集合中删除集合c中包含的元素
    • boolean retainAll(Collection<?> c); 只保留c集合中包含的元素
    • void clear(); 清除集合中所有元素
    • boolean contains(Object o); 判断集合中是否包含元素o
    • boolean containsAll(Collection<?> c); 判断集合是否完整包含集合c中全部元素
    • Iterator<E> iterator(); 返回迭代器
    • int size(); 返回集合数量
    • boolean isEmpty(); 判断集合是否为空
    • Object[] toArray(); 将集合中元素转换成数组
    • <T> T[] toArray(T[] a); 将集合中元素转换成T类型的数组

    AbstractCollection 抽样类

    1. 强制子类复写Iterator<E> iterator() 和int size() 方法
    2. boolean add(E e) 方法直接抛出异常,如果不复写这个方法,那么集合是不能添加数据的。
    3. boolean addAll(Collection<? extends E> c) 方法,是遍历集合,然后调用add方法,将元素添加到集合中,所以也会抛出异常。
    4. remove(Object o) 方法,是调用迭代器的remove()实现的。
    5. removeAll(Collection<?> c)方法,是通过迭代器遍历集合,然后使用集合c的contains方法,判断元素在集合中是否也存在,是的话就调用迭代器的remove()方法,删除这个元素。
    6. retainAll(Collection<?> c)方法,与removeAll方法流程大体一样,只不过它是判断集合c中不包含就调用迭代器的remove()方法,删除这个元素。
    7. void clear(); 通过迭代器遍历集合,然后调用r迭代器的remove()方法,删除每个元素
    8. boolean contains(Object o); 通过迭代器遍历集合,查看是否有与o相等的元素。
    9. boolean containsAll(Collection<?> c); 遍历集合c,拿集合c中每个元素调用本集合的contains方法。如果有返回false,那么containsAll方法就直接返回false。只有全部通过才返回true。
    10. Object[] toArray(); 和<T> T[] toArray(T[] a); 通过迭代器将集合转化成对应数组。

    例子

    下面用一个例子来表示:

        public static void main(String[] args) {
            // 创建AbstractCollection实例,需要复写iterator和size,这是一个不可修改集合
            Collection<Integer> collection = new AbstractCollection() {
                int[] data = {1,2,3,4,5}; // 集合中的元素
                @Override
                public Iterator iterator() {
                    return new Iterator() {
    
                        int cursor;
                        @Override
                        public boolean hasNext() {
                            return cursor != data.length;
                        }
    
                        @Override
                        public Object next() {
                            return data[cursor++];
                        }
                    };
                }
    
                @Override
                public int size() {
                    return data.length;
                }
            };
    
            // 遍历集合
            for (int i : collection) {
                System.out.println(i);
            }
            // 测试contains方法
            System.out.println(collection.contains(3));
            System.out.println(collection.contains(10));
    
            List<Integer> other = new ArrayList<>();
            other.add(2);
            other.add(3);
            other = other.subList(0,1);
            for (int i : other) {
                System.out.println(i);
            }
            // 测试containsAll方法
            System.out.println(collection.containsAll(other));
        }
    

    相关文章

      网友评论

        本文标题:Java 集合框架_开篇

        本文链接:https://www.haomeiwen.com/subject/zuyiwxtx.html