A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

[重学Java基础][Java内置工具类][Part.2] Collections工具类

===========





Collection与Collections

Collection是集合的最顶层接口,提供了对集合对象进行基本操作的通用接口方法。为各种具体的集合提供了最大化的统一操作方式。
Collentions是一个工具类。它包含各种有关集合操作的静态多态方法,此类的构造方法为private,不能被实例化。

Collections工具类的方法
基于Java 9

根据是否有返回值 可以把Collections工具类的方法
大体上分为两类 一组是返回集合对象的方法
另一组是对传入的集合对象操作的方法

返回集合对象的方法

**这些方法都返回一个重新包装的集合对象 也称之为视图对象
提供了许多重要功能 减少了繁琐的数据结构构建开销**

空集合

空集合指的是没有元素在这些集合中,特别需要主要的是返回的集合都是只读的。只要更改值就会抛出UnsupportedOperationException异常 如果获取元素则抛出下标越界异常

Collections.EMPTY_LIST,Collections.emptyList()//返回只读 的空LIST 集合 Collections.EMPTY_MAP,Collections.emptyMap()//返回只读 的空MAP集合 Collections.EMPTY_SET,Collections.emptySet()//返回只读 的空SET集合
  • 1
  • 2
  • 3
  • 4

其实底层有一个Empty*类,就是返回一个新的实例。

看源码 以emptyList为例

返回一个内部类 EmptyList public static final <T> List<T> emptyList() {        return (List<T>) EMPTY_LIST;    }EmptyList和ArrayList 一样 都实现了RandomAccess接口 扩展AbstractList抽象类不一样的是EmptyList内部没有实际的数据序列所有方法都返回空或者0 private static class EmptyList<E>  extends AbstractList<E>  implements RandomAccess, Serializable {  private static final long serialVersionUID = 8842843931221139166L;  ……
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

空集合主要用于一些需要返回集合的场合

传统的 返回一个新的集合或者匿名集合 无形中增加了内存占用

return new ArrayList<T>();
  • 1

而返回空集合

return Collections.emptyList();
  • 1

由源码可知是Collections类的一个static内部类的引用 无论有多少个引用 所占内存都是一样的

单元素集合

Collections中的单元素集合指的是集合只有一个元素而且集合只读。

Collections.singletonList //用来生成只读 的单一元素的List Collections.singletonMap //用来生成只读 的单Key和Value组成的Map Collections.singleton //用来生成只读 的单一元素的Set
  • 1
  • 2
  • 3
  • 4

创建一个String泛型限定的单一元素集合

        String string="This is a string";        List<String> list=Collections.singletonList(string);
  • 1
  • 2
  • 3
  • 4

此单元素集合不能新增 移除元素 否则会抛出异常

其实底层有一个singleton*类,调用方法就返回一个实例

源码 SingletonList为例

private static class SingletonList<E>        extends AbstractList<E>        implements RandomAccess, Serializable {        private static final long serialVersionUID = 3093736618740652951L;        相比空集合 单元素集合有了实际的数据序列        private final E element;        ……    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

单元素集合同样实现了RandomAccess接口 扩展AbstractList抽象类
但是重写的集合方法 不支持所有对集合对象的改变操作

运用单元素集合 可以便捷的从集合中移除重复元素

示例

List<String> list = new ArrayList<String>();list.add("abc");list.add(null);list.add("def");list.removeAll(Collections.singletonList(null));System.out.println(list);  //[abc, def]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
只读集合

这些集合一旦初始化以后就不能修改,任何修改这些集合的方法都会抛出UnsupportedOperationException异常。

Collections.unmodifiableCollection()Collections.unmodifiableList()Collections.unmodifiableMap() Collections.unmodifiableSet() Collections.unmodifiableSortedMap() Collections.unmodifiableSortedSet()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以上方法都顾名思义

创建一个只读的链表

List list=new LinkedList<String>();List unmodList=Collections.unmodifiableList(list);
  • 1
  • 2
  • 3

unmodList就成为一个被构建出来的只读集合 unmodList等只读集合内部引用了原始集合
对外提供的是自己的方法 保证不能修改原集合

但是 如果修改了原始集合 则此只读集合也会改变 类似Arrays.asList()

Checked集合

Checked集合具有检查插入集合元素类型的特性,例如当我们设定checkedList中元素的类型是String的时候,如果插入起来类型的元素就会抛出ClassCastExceptions异常。

Collections.checkedCollection()Collections.checkedList() Collections.checkedMap() Collections.checkedSortedMap() Collections.checkedSortedSet()Collections.checkedNavigableSet() Collections.checkedNavigableSet()  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以上方法都顾名思义

示例

    List<Integer> integerList=new ArrayList<>();    List checkedlist=Collections.checkedList(integerList,Integer.class);
  • 1
  • 2
  • 3

此时 checkedlist不能插入非Integer类型 否则报ClassCastException

同步集合Collections.synchronizedCollection()Collections.synchronizedList() Collections.synchronizedMap() Collections.synchronizedSortedMap() Collections.synchronizedSortedSet()Collections.synchronizedNavigableMap()Collections.synchronizedNavigableSet()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Collections的synchronizedXxxxx系列方法顾名思义会返回同步化集合类(SynchronizedMap,
SynchronizedList等等)。
这些集合类内部实现都是通过一个mutex(互斥体)来实现对这些集合操作的同步化。
保证了多线程并发安全

synchronizedList 可以代替过时的Vector向量类
当然同样的CopyOnWriteArrayList 也可以代替Vector向量类
Collections.synchronizedList() 包装方法返回的同步列表在写操作上更快
而CopyOnWriteArrayList在读操作上更迅速
另外 如果你需要一个并发安全的LinkedList 那么显然只能使用synchronizedList()

Collections.synchronizedMap()方法可以代替过时的HashTable
当然 现在更推荐的是使用改进的并发散列表ConcurrentHashMap
来代替过时的 HashTable类

对传入的集合对象操作的方法替换查找
  • fill()方法 使用指定元素替换指定列表中的所有元素
Collections.fill(List< ? super T> list, T obj)
public static <T> void fill(List<? super T> list, T obj) {    int size = list.size();    if (size < FILL_THRESHOLD || list instanceof RandomAccess) {        for (int i=0; i<size; i++)            list.set(i, obj);    } else {        ListIterator<* super T> itr = list.listIterator();        for (int i=0; i<size; i++) {            itr.next();            itr.set(obj);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里可以看出有一个域值FILL_THRESHOLD,这个值为25,如果小于25,或者实现随机访问接口的,直接set。否则使用迭代器的set。

List<String> stringList=new ArrayList<>(10); …… Collections.fill(stringList,"fill string");
  • 1
  • 2
  • 3
  • 注意 Collections.fill()方法只会填充list的实际size大小的数据

  • frequency()方法 返回指定 collection 中等于指定对象的元素数


Collections.frequency(Collection< ? > c, Object o)
public static int frequency(Collection<?> c, Object o) {    int result = 0;    if (o == null) {        for (Object e : c)            if (e == null)                result++;    } else {        for (Object e : c)            if (o.equals(e))                result++;    }    return result;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

为null的时候用==判断,否则用equals(Object中equals也是用的==判断)。

返回指定源列表中指定元素位置

  • indexOfSubList—— 返回指定源列表中第一次出现指定目标列表的起始位置,如果没有出现这样的列表,则返回 -1。

Collections.indexOfSubList(List< ? > source, List< ? > target)
   public static int indexOfSubList(List<?> source, List<?> target) {        int sourceSize = source.size();        int targetSize = target.size();        int maxCandidate = sourceSize - targetSize;        如果列表大小小于35或者列表支持随机访问        if (sourceSize < INDEXOFSUBLIST_THRESHOLD ||            (source instanceof RandomAccess&&target instanceof RandomAccess)) {        nextCand:            for (int candidate = 0; candidate <= maxCandidate; candidate++) {                for (int i=0, j=candidate; i<targetSize; i++, j++)                    if (!eq(target.get(i), source.get(j)))                        continue nextCand;  // 元素未匹配到 回退到起始位置                return candidate;  // 所有元素比较完毕            }        } else {  不支持随机访问 使用迭代器遍历            ListIterator<?> si = source.listIterator();        nextCand:            for (int candidate = 0; candidate <= maxCandidate; candidate++) {                ListIterator<?> ti = target.listIterator();                for (int i=0; i<targetSize; i++) {                    if (!eq(ti.next(), si.next())) {                        //回退原列表迭代器到起始位置                        for (int j=0; j<i; j++)                            si.previous();                        continue nextCand;                    }                }                return candidate;            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • lastIndexOfSubList——返回指定源列表中最后一次出现指定目标列表的起始位置,如果没有出现这样的列表,则返回-1。
    和indexOfSubList方法类似 反向搜索
Collections.lastIndexOfSubList(List< ? > source, List< ? > target)


  • max方法—— 返回给定 collection 的最大元素。

Collections.max(Collection< ? extends T> coll, Comparator< ? super T> comp)
public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {    if (comp==null)        return (T)max((Collection) coll);    Iterator<? extends T> i = coll.iterator();    T candidate = i.next();    while (i.hasNext()) {        T next = i.next();        if (comp.compare(next, candidate) > 0)            candidate = next;    }    return candidate;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • min方法—— 返回给定 collection 的最小元素。
Collections.min(Collection< ? extends T> coll, Comparator< ? super T> comp)

max,min方法显然需要排序 如果集合中的对象没有实现comparator接口,
就以自然顺序排序,如果显示的制指定了Comparator比较器
则使用比较器提供的比较方法比较

注:comparable和comparator的区别。
Comparator在util包下,Comparable在lang包下。
java中的对象排序都是以comparable接口为标准的。
comparator是在对象外部实现排序。

替换
  • replaceAll——使用另一个值替换列表中出现的所有某一指定值。
    Collections.replaceAll(List list, T oldVal, T newVal)
    原理也和其他类似 先迭代比较 再替换

排序

  • reverse——对List中的元素倒序排列

Collections.reverse(List< ? > list)
public static void reverse(List<?> list) {    int size = list.size();    if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {        for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)            swap(list, i, j);    } else {        ListIterator fwd = list.listIterator();        ListIterator rev = list.listIterator(size);        for (int i=0, mid=list.size()>>1; i<mid; i++) {            Object tmp = fwd.next();            fwd.set(rev.previous());            rev.set(tmp);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

都是分为两种情况进行处理,支持随机访问的使用swap首尾交换
使用迭代器的需要两个迭代器,一个前向迭代 一个后向迭代
使用中间值进行交换

  • shuffle——对List中的元素随机排列 类似Arrays.shuffle()方法
Collections.shuffle(List< ? > list)
  • sort——对List中的元素排序
Collections.sort(List list)
Collections.sort(List list, Comparator
public static <T extends Comparable<? super T>> void sort(List<T> list) {    list.sort(null);}public static <T> void sort(List<T> list, Comparator<? super T> c) {    list.sort(c);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

sort方法底层其实就是调用List接口的sort方法
java8中List接口使用default关键字实现了sort方法
使用sort方法可以对指定列表按升序进行排序。
如果没有指定比较器Comparator,则列表中的所有元素都必须实现Comparable接口
否则会使用自然顺序进行排序

也可以 使用Collections.sort(List list, Comparator

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

发现其实底层是调用的Arrays.sort()方法,然后调用迭代器给现在的list赋值。

  • reverseOrder 反向比较器
Collections.reverseOrder(Comparator cmp)
返回一个和入参的 Comparator cmp比较器 反向的比较器
其他

  • copy——复制源集合中元素到新集合中

Collections.copy(List< ? super T> dest, List< ? extends T> src)
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {        int srcSize = src.size();        if (srcSize > dest.size())            throw new IndexOutOfBoundsException("Source does not fit in dest");        if (srcSize < COPY_THRESHOLD ||            (src instanceof RandomAccess && dest instanceof RandomAccess)) {            for (int i=0; i<srcSize; i++)                dest.set(i, src.get(i));        } else {            ListIterator<? super T> di=dest.listIterator();            ListIterator<? extends T> si=src.listIterator();            for (int i=0; i<srcSize; i++) {                di.next();                di.set(si.next());            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
源码也都是类似的 分策略处理 集合大小小于25或者支持随机访问 则直接迭代复制 否则使用迭代器复制
  • swap——交换List中某两个指定下标位元素在集合中的位置。
Collections.sort()
public static void swap(List<?> list, int i, int j) {    final List l = list;    l.set(i, l.set(j, l.get(i)));    巧妙的利用了List实现类的set新值返回旧值的功能}private static void swap(Object[] arr, int i, int j) {    对于数组 只能中值交换了    Object tmp = arr;    arr = arr[j];    arr[j] = tmp;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

同样,对于链表和数组有不同的操作方式。

  • rotate——循环移动。
Collections.rotate(List< ? > list, int distance)

假设list包含[t, a, b, k, s]。
在调用Collection.rotate(list, 1)
或者 Collection.rotate(list, -4) 后
list将包含[s, t, a, b, k]。

代码示例

        List<String> stringList=new ArrayList<>(10);        stringList.add("a");        stringList.add("b");        stringList.add("c");        stringList.add("e");        stringList.add("f");        System.out.println(Arrays.deepToString(stringList.toArray()));        //[a, b, c, e, f]        Collections.rotate(stringList,2);        System.out.println(Arrays.deepToString(stringList.toArray()));        //[e, f, a, b, c]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • addAll()全部加入方法 将所有元素全部加入指定集合的方法
    Collections.addAll(Collection<* super T> c, T… elements);


代码示例        List<String> stringList=new ArrayList<>(10);        stringList.add("a");        stringList.add("b");        stringList.add("c");        stringList.add("e");        stringList.add("f");        Collections.addAll(stringList,"g","h","j");        System.out.println(Arrays.deepToString(stringList.toArray()));        //[a, b, c, e, f, g, h, j]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
addAll()方法的源码倒是很简单    public static <T> boolean addAll(Collection<* super T> c, T... elements) {        boolean result = false;        for (T element : elements)            result |= c.add(element);        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

显然这个方法 只能是实现了Collection接口的列表才能使用



  • binarySerach() 二分搜索方法 返回给定值在集合中的下标序数

Collections.binarySearch(List< ? extends Comparable< ? super T>> list, T key)
Collections.binarySearch(List< ? extends T> list, T key, Comparator< ? super T> c)
这个方法只能是实现了Collection接口的列表才能使用
        List<String> stringList=new ArrayList<>(10);        stringList.add("a");        stringList.add("b");        stringList.add("c");        stringList.add("e");        stringList.add("f");        //stringList.add("g");        stringList.add("h");       int a=Collections.binarySearch(stringList,"a");       int b=Collections.binarySearch(stringList,"b");       int i=Collections.binarySearch(stringList,"i");        System.out.println(a);        System.out.println(b);        System.out.println(g);        System.out.println(i);        //0        //1        //-6        //-7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
可以看到采取的策略和Arrays.binarySerach()方法是的一致 集合中存在的元素 返回下标 集合中不存在的元素 返回负的距离标定点位置 源码 以ArrayList为例 典型的二分搜索所犯 很好理解    private static <T>    int indexedBinarySearch(List< ? extends Comparable< ? super T>> list, T key) {        int low = 0;        int high = list.size()-1;        while (low <= high) {            int mid = (low + high) >>> 1;            Comparable< ? super T> midVal = list.get(mid);            int cmp = midVal.compareTo(key);            if (cmp < 0)                low = mid + 1;            else if (cmp > 0)                high = mid - 1;            else                return mid; // key found        }        return -(low + 1);  // key not found    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19


  • nCopies() N倍复制方法 返回包含有N个指定集合对象的集合

Collections.List nCopies(int n, T o)
N倍复制方法 返回一个包含有N个入参的集合对象的新集合
当然 这个新集合也是不可变的

示例

        List<List<String>> anotherList=Collections.nCopies(5,stringList);        anotherList.forEach(x-> System.out.println(x));        System.out.println(anotherList.size());
  • 1
  • 2
  • 3
  • 4
  • 5
结果[a, b, c, e, f, g, h][a, b, c, e, f, g, h][a, b, c, e, f, g, h][a, b, c, e, f, g, h][a, b, c, e, f, g, h]5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
源码 其实是创建了一个新的CopiesList对象 此CopiesList类通过final E element来保存对象public static <T> List<T> nCopies(int n, T o) {        if (n < 0)            throw new IllegalArgumentException("List length = " + n);        return new CopiesList<>(n, o);    }    private static class CopiesList<E>        extends AbstractList<E>        implements RandomAccess, Serializable    {        private static final long serialVersionUID = 2739099268398711800L;        final int n;        final E element;        初始化时只保存了一个对象        CopiesList(int n, E e) {            assert n >= 0;            this.n = n;            element = e;        }        ……        get元素时 先检查index是否越界         然后直接返回element对象        public E get(int index) {        if (index < 0 || index >= n)        throw new IndexOutOfBoundsException("Index: "+index+                                                ", Size: "+n);        return element;        }        ……    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

这个类很巧妙 其实只保存了一个element对象 和复制倍数n
调用get方法时永远返回element对象



  • disjoin()互斥方法 返回两个集合是否不存在交集

Collections.disjoint(Collection< ? > c1, Collection< ? > c2)
        List<String> stringList=new ArrayList<>(10);        stringList.add("a");        stringList.add("b");        stringList.add("c");        stringList.add("e");        stringList.add("f");        stringList.add("g");        stringList.add("h");        boolean bp=Collections.disjoint(stringList,stringList);        //false         List list=new LinkedList();        boolean bp2=Collections.disjoint(list,list);        //true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

两个含有相同元素的的集合 disjoin返回false 说明不互斥 存在交集
注意 而两个空集合 disjoin返回true

  • list()方法 枚举对象转为数组列表

    Collections.list(Enumeration e)
    不常用方法 主要是Enumeration类功能已经被迭代器替代了
    主要用于一些遗留Enumeration对象的场合
  • newSetFormMap()方法 根据指定的map对象创建对应的Set对象


Collections.newSetFromMap(Map< E, Boolean> map)

众所周知 set就是个套着马甲的Map 例如TreeMap和HashMap
这些Set类都是基于对应的Map类实现的
因此它们和对应的Map类保持相同的算法复杂度以及并发特性。

所以可以使用此方法来创建对应的Set对象
Java 7 以后提供了更安全的并发散列表concurrentHashMap
但是并没有对应的set

可以自己创建一个

Set concurrentHashSet=Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>());
  • 1
  • 2


【转载】原文地址:https://blog.csdn.net/u011863951/article/details/80725107

3 个回复

倒序浏览
回复 使用道具 举报
奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马