zoukankan      html  css  js  c++  java
  • Iterable、Collection、AbstractConlltion、List学习笔记

    类图

    Iterable

    集合框架最顶端的接口,实现接口的遍历。迭代器接口

    可进行迭代的接口

    public interface Iterable<T> {
        Iterator<T> iterator();
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
        default Spliterator<T> spliterator() {
            return Spliterators.spliteratorUnknownSize(iterator(), 0);
        }
    }
    

    iterator()

    这个方法返回的是Iterator的实例。Iterator也是一个接口,为什么不将两个接口合并为一个那?

    原因是实现了Iterable的类可以在实现多个Iterator内部类,例如LinkedList中的ListItrDescendingIterator两个内部类,就分别实现了双向遍历和逆序遍历。通过返回不同的Iterator实现不同的遍历方式,这样更加灵活。如果把两个接口合并,就没法返回不同的Iterator实现类了。

    public class MyIterator implements Iterable<String>{
        private String[] words = "Test iterator in Iterable".split(" ");
        //默认的迭代器,前向遍历   正常的
        @Override
        public Iterator<String> iterator() {
            //匿名内部类
            return new Iterator<String>() {
                private int index = 0;
                @Override
                public boolean hasNext() {return index < words.length;}
                @Override
                public String next() { return words[index++];    }
                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
        //反向迭代器
        public Iterable<String> reverseIterator() {
            return new Iterable<String>() {
                @Override
                public Iterator<String> iterator() {
                    return new Iterator<String>() {
                        private int index = words.length - 1;
                        @Override
                        public boolean hasNext() {return index > -1; }
                        @Override
                        public String next() {return words[index--]; }
                        @Override
                        public void remove() { // Not implemented
                            throw new UnsupportedOperationException();
                        }
                    };
                }
            };
        }
        //随机迭代器,注意这里不是创建一个新的Iterator,而是返回了一个打乱的List中的迭代器
        public Iterable<String> randomized() {
            return new Iterable<String>() {
                @Override
                public Iterator<String> iterator() {
                    List<String> shuffled = new ArrayList<>(Arrays.asList(words));
                    Collections.shuffle(shuffled, new Random(47));
                    return shuffled.iterator();
                }
            };
        }
    }
    

    forEach(Consumer<? super T> action)

    循环容器中的元素,并对每个元素进行 action 操作,容器中不能有空元素,否则或抛出 NullPointerException 指针异常。

        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.forEach(System.out::println);
        }
    

    spliterator()

    Spliterator接口是1.8新加的接口,字面意思可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,既可以实现多线程操作提高效率,又可以避免普通迭代器的fail-fast机制所带来的异常。Spliterator可以配合1.8新加的Stream进行并行流的实现,大大提高处理效率。

    Collection

    Collection是最基本集合接口,接口中为子集合定义了各种基础的方法,方便我们使用。

    int size();

    方法返回一个int类型的值,代表集合中元素的个数,如果集合中的元素个数超过正数的最大值,那么只能返回正数的最大值。

    boolean isEmpty();

    返回一个boolean类型的值,代表当前集合是否为集合。

    boolean contains(Object o)

    返回boolean值代表集合中是否含有这个元素,这个方法比较依赖对象中的重写过的equals方法,如果没有重写equals方法,那么代表是否含有该内存位置的对象。参数o如果不是Collection指定的泛型类型,会报ClassCastException异常。参数为null,但是Collection要求不能存在null值的时候报NullPointerException异常。

    Object[] toArray()、 T[] toArray(T[] a)

    两个方法都是将集合中的元素以数组的形式返回,如果集合规定了一个迭代器,那么集合返回的数组会按照迭代器规定的顺序排列后返回。第二个方法会返回一个指定类型的结合,这里的参数T[] a的作用只是提供一个泛型的类型以及一个数组的长度,如果集合的长度大于传入参数a数组的长度,返回的数组长度与集合长度相同,如果集合长度小于传入参数a数组的长度,返回的数组长度与参数a数组的长度相同,但是比集合元素多出来的所有位置的都为null。

    如果指定数组a的运行时类型跟数组内元素的类型不同时,会报java.lang.ArrayStoreException如果数组a为null,报NullPointerException

    boolean add(E e)

    向集合中添加元素

    boolean addAll(Collection<? extends E> c)

    向集合中添加参数集合的所有元素

    异常:

    1. 如果集合不支持添加操作,报UnsupportedOperationException

    2. 如果e的类型被集合指定不允许添加,报ClassCastException

    3. 如果元素的某些属性导致元素不能被添加到集合中,报IllegalArgumentException

    4. 如果元素不能在这个时候被添加到集合中,报IllegalStateException

    5. 如果元素为null,但是集合中不允许出现null元素,报NullPointerException.

    boolean remove(Object o)

    移除集合中的指定元素

    boolean removeAll(Collection<?> c)

    移除原集合中所有在参数集合中出现过的元素

    异常:

    1. 指定的元素o的运行时类型与集合指定的泛型不匹配时,报ClassCastException

    2. 指定对象中包含一到多个null元素,但是集合中不允许出现null时,报NullPointerException

    3. 如果该集合不支持移除操作,报UnsupportedOperationException

    boolean containsAll(Collection<?> c)

    检验该集合是否包含指定集合的所有元素

    异常:

    1. 参数集合中有元素的类型跟原集合指定的泛型类型不一致时,报ClassCastException

    2. 参数集合中包含null元素,但原集合不允许出现null时,报NullPointerException

    default boolean removeIf(Predicate<? super E> 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;
        }
    

    删除集合中所有满足删除条件的元素。这个是JDK1.8新加的方法,配合Predicate接口和lambda表达式进行使用。

    异常:

    1. 如果判断条件为null时,报NullPointerException

    2. 如果满足条件的元素不能被移除,报UnsupportedOperationException

    boolean retainAll(Collection<?> c)

    原集合只保留在指定集合中存在的元素,简单点说就是取交集,包含contains方法和remove方法的所有可能抛出异常

    clear()

    清楚集合内的所有元素,异常:如果clear操作不被集合支持,抛出UnsupportedOperationException异常

    default Stream stream()

    获取一个单通道的流处理

    default Stream parallelStream()

    获得一个多通道的流处理器

    AbstractConlltion

    AbstractCollection是Collection的一个一级实现,具体实现了Collection中的几个通用的方法。
    增加了一个protect修饰的构造方法给子类使用

      protected AbstractCollection() {
        }
    

    看到一个新增的属性:最大数组的长度

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    

    List

    [图片上传失败...(image-c6c496-1586074782308)]

    通过List源码注释大体可以知道List集合接口继承至Collection接口,有序集合、允许重复、允许空值。

    除了继承Collection的方法为,下面是List比Collection多的方法

    replaceAll

        default void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            final ListIterator<E> li = this.listIterator();
            while (li.hasNext()) {
                li.set(operator.apply(li.next()));
            }
        }
    

    1.8中出现的新方法,接收lambda表达式生成的一个UnaryOperator对象,对list中的每个对象进行替换操作

    List<String> list = new ArrayList<>();
    list.add("hello");
    list.add("hello java");
    list.replaceAll(s -> s.replace("e", "1"));
    System.out.println(list.toString());
    

    sort

     @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);
            }
        }
    

    1.8中新增的方法,让list的调用自己的方法就能实现排序,不必再实现comparable接口或者使用Collections类的排序方法进行排序,其实底层调用了Arrays方法对list进行排序,方法同样支持lambda的函数式编程,

    List<Integer> list = new ArrayList<>();
    for (int i = 10; i > 0; i--) {
        list.add(i);
    }
    System.out.println(list);
    list.sort((o1, o2) -> o1 - o2);
    // Collections.sort(list);//与上一行作用相同
    System.out.println(list);
    

    boolean addAll(int index, Collection<? extends E> c)

    将指定的集合插入到该序列中的指定的index位置

    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)

    获取指定元素最后一次在序列中出现的位置

    ListIterator listIterator();

    返回列表元素的列表迭代器(按适当顺序)

    ListIterator listIterator(int index)

    返回列表元素的列表迭代器(按适当顺序)

    List subList(int fromIndex, int toIndex)

    返回该序列中从fromIndex到toIndex中的一个子序列(子列表其实还是这个列表一部分,改变了会改变该序列)

    AbstractList

    AbstractList,AbstractList继承AbstractCollection并实现List接口,是ArrayList、Vector和Stack等重要类的超类。
    它实现了 List 的一些位置相关操作(比如get,set,add,remove),是第一个实现随机访问方法的集合类,但不支持添加和替换。通过源码就可以看出

     public boolean add(E e) {
            add(size(), e);
            return true;
        }
          public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
           public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }
    

    void clear()

    实现AbstractCollection的clear方法,实际调用新增的removeRange方法实现集合清空。

    public void clear() {
         removeRange(0, size());
     }
     protected void removeRange(int fromIndex, int toIndex) {
       ListIterator<E> it = listIterator(fromIndex);
       for (int i=0, n=toIndex-fromIndex; i<n; i++) {
           it.next();
           it.remove();
       }
    }
    

    boolean addAll(int index, Collection<? extends E> c)

    public boolean addAll(int index, Collection<? extends E> c) {
        //检查index的值是否在集合范围内
         rangeCheckForAdd(index);
         boolean modified = false;
         //将指定集合元素从index起依次添加进原集合中
         for (E e : c) {
             add(index++, e);
             modified = true;
         }
         //返回是否更改标识
         return modified;
     }
    

    Iterator iterator()

    public Iterator<E> iterator() {
          return new Itr();
      }
      private class Itr implements Iterator<E> {
            //next方法即将会返回的元素下标
            int cursor = 0;
         //最近一次返回的元素的下标,执行删除操作会变回-1
            int lastRet = -1;
            //期望的修改次数,如果两个值不相同时,迭代器就发生了并发修改
            //protected transient int modCount = 0;
            //modCount是AbstractList中一个transient修饰的变量,表示集合结构大小变化的次数
            int expectedModCount = modCount;
            public boolean hasNext() {
                return cursor != size();
            }
            public E next() {
                //检查修改期望值,防止并发修改的现象出现
                checkForComodification();
                try {
                    //每次执行next操作获取游标位置的元素,同时lastRet变为当前游标值后游标下移
                    int i = cursor;
                    E next = get(i);
                    lastRet = i;
                    cursor = i + 1;
                    return next;
                } catch (IndexOutOfBoundsException e) {
                     //二次检验确定是否是并发引起的问题
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public void remove() {
                if (lastRet < 0)
                    //当lastRet为-1两种情况,一种未执行过一次next操作,另一种执行过remove操作后未执行过next操作
                //两种情况下如果执行remove操作,前者游标尚未移动影响后续next操作,后者当前位置元素已经删除
                    throw new IllegalStateException();
                 //每次修改前检查,尽可能防止并发修改现象出现
                checkForComodification();
                try {
                     //移除游标前一位置的元素,同时游标前移,lastRet变为-1,期望改变次数同步
                    AbstractList.this.remove(lastRet);
                    if (lastRet < cursor)
                        cursor--;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException e) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    

    listIterator、listIterator(final int index)

    public ListIterator<E> listIterator() {
        return listIterator(0);
    }
    public ListIterator<E> listIterator(final int index) {
        rangeCheckForAdd(index);
        return new ListItr(index);
    }
        private class ListItr extends Itr implements ListIterator<E> {
            //游标与Itr稍微区别是从传入的Index开始
            ListItr(int index) {
                cursor = index;
            }
    		//增加了是否有前一个元素的方法
            public boolean hasPrevious() {
                return cursor != 0;
            }
     		//获取前一个元素
            public E previous() {
                checkForComodification();
                try {
                    int i = cursor - 1;
                    E previous = get(i);
                    lastRet = cursor = i;
                    return previous;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor-1;
            }
    //新增的set方法,元素替换,同样不能出现在游标未移动或删除元素操作之后原因与上面的next方法一致
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.set(lastRet, e);
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
     //新增的add方法,实现方式与Itr中remove方法类似,添加的元素出现在原游标的位置,同时游标后移
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    AbstractList.this.add(i, e);
                    lastRet = -1;
                    cursor = i + 1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    

    从Itr和ListItr的源码中我们可以看到list中两种迭代器在多个操作中使用了双重检验的方式以避免出现多个线程同时操作迭代器的情况出现,所以在使用的时候都需要避免多线程的操作。

    subList(int fromIndex, int toIndex)

     public List<E> subList(int fromIndex, int toIndex) {
            return (this instanceof RandomAccess ?
                    new RandomAccessSubList<>(this, fromIndex, toIndex) :
                    new SubList<>(this, fromIndex, toIndex));
    }
    
    • 根据list是否实现RandomAccess接口,返回随机访问的子list或者普通的子list。首先是RandomAccess,这是一个标记接口,用于标明实现该接口的集合支持快速随机访问,主要目的是使算法能够在随机和顺序访问的list中表现的更加高效。通俗点说ArrayList、Vector、Stack等实现了RandomAccess接口,我们可以通过下标快速找到对应的元素,LinkedList等没有实现RandomAccess接口,所以想要找到指定位置的元素就要麻烦一些。

    • sublist其实跟原来的list使用的是同样的数据,只是在原来List的基础之上增加了一个偏移量的概念,让生成的子集合能够正确的对应到原来的位置,其中一系列的方法跟AbstractList的实现方法基本一致,只是让偏移量这个东西参与到其中而且在每次进行子list的操作之前都会对modCount进行一次检查保证安全。也就是说如果你动了子List中的元素,原List中的元素也会跟着发生变化。

    RandomAccess、Cloneable、Serializable接口的作用

    这三个几口是标记接口,接口的非典型用法

    RandomAccess

    随机访问任意下标元素都比较快,当要实现某些算法时,会判断当前类是否实现了RandomAccess接口。

    binarySearch()方法中,它要判断传入的list 是否 RamdomAccess 的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法

      public static <T>
        int binarySearch(List<? extends Comparable<? super T>> list, T key) {
            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
                return Collections.indexedBinarySearch(list, key);
            else
                return Collections.iteratorBinarySearch(list, key);
        }
    

    ArrayList 实现了 RandomAccess 接口, 而 LinkedList 没有实现。为什么呢?我觉得还是和底层数据结构有关!ArrayList 底层是数组,而 LinkedList 底层是链表。数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。,ArrayList 实现了 RandomAccess 接口,就表明了他具有快速随机访问功能。 RandomAccess 接口只是标识,并不是说 ArrayList 实现 RandomAccess 接口才具有快速随机访问功能的!

    Cloneable

    支持clone操作

    Serializable

    标志支持序列化功能

  • 相关阅读:
    vc++ 最小化到托盘的详细实现
    WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解(转载)
    windows 进程间通讯方法
    C++中各种数据量类型转换
    WTL 自绘 进度条Progressbar
    C++ 读取XML文件(tinyXML库的应用)
    WTL 自定义 Button类-自绘
    设计模式课程 设计模式精讲 27-3 状态模式源码解析
    设计模式课程 设计模式精讲 27-2 状态模式coding
    设计模式课程 设计模式精讲 26-3 访问者模式源码解析
  • 原文地址:https://www.cnblogs.com/yangk1996/p/12653486.html
Copyright © 2011-2022 走看看