|
[重学Java基础][Java内置工具类][Part.2] Collections工具类 ===========
Collection与CollectionsCollection是集合的最顶层接口,提供了对集合对象进行基本操作的通用接口方法。为各种具体的集合提供了最大化的统一操作方式。
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集合其实底层有一个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; ……空集合主要用于一些需要返回集合的场合 传统的 返回一个新的集合或者匿名集合 无形中增加了内存占用 return new ArrayList<T>();而返回空集合 return Collections.emptyList();由源码可知是Collections类的一个static内部类的引用 无论有多少个引用 所占内存都是一样的 单元素集合Collections中的单元素集合指的是集合只有一个元素而且集合只读。 Collections.singletonList //用来生成只读 的单一元素的List Collections.singletonMap //用来生成只读 的单Key和Value组成的Map Collections.singleton //用来生成只读 的单一元素的Set创建一个String泛型限定的单一元素集合 String string="This is a string"; List<String> list=Collections.singletonList(string);此单元素集合不能新增 移除元素 否则会抛出异常 其实底层有一个singleton*类,调用方法就返回一个实例 源码 SingletonList为例 private static class SingletonList<E> extends AbstractList<E> implements RandomAccess, Serializable { private static final long serialVersionUID = 3093736618740652951L; 相比空集合 单元素集合有了实际的数据序列 private final E element; …… }单元素集合同样实现了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]只读集合这些集合一旦初始化以后就不能修改,任何修改这些集合的方法都会抛出UnsupportedOperationException异常。 Collections.unmodifiableCollection()Collections.unmodifiableList()Collections.unmodifiableMap() Collections.unmodifiableSet() Collections.unmodifiableSortedMap() Collections.unmodifiableSortedSet() 以上方法都顾名思义 创建一个只读的链表 List list=new LinkedList<String>();List unmodList=Collections.unmodifiableList(list);unmodList就成为一个被构建出来的只读集合 unmodList等只读集合内部引用了原始集合
对外提供的是自己的方法 保证不能修改原集合 但是 如果修改了原始集合 则此只读集合也会改变 类似Arrays.asList() Checked集合Checked集合具有检查插入集合元素类型的特性,例如当我们设定checkedList中元素的类型是String的时候,如果插入起来类型的元素就会抛出ClassCastExceptions异常。 Collections.checkedCollection()Collections.checkedList() Collections.checkedMap() Collections.checkedSortedMap() Collections.checkedSortedSet()Collections.checkedNavigableSet() Collections.checkedNavigableSet() 以上方法都顾名思义 示例 List<Integer> integerList=new ArrayList<>(); List checkedlist=Collections.checkedList(integerList,Integer.class);此时 checkedlist不能插入非Integer类型 否则报ClassCastException 同步集合Collections.synchronizedCollection()Collections.synchronizedList() Collections.synchronizedMap() Collections.synchronizedSortedMap() Collections.synchronizedSortedSet()Collections.synchronizedNavigableMap()Collections.synchronizedNavigableSet()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); } }}这里可以看出有一个域值FILL_THRESHOLD,这个值为25,如果小于25,或者实现随机访问接口的,直接set。否则使用迭代器的set。 List<String> stringList=new ArrayList<>(10); …… Collections.fill(stringList,"fill string");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;}为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;}- 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)
原理也和其他类似 先迭代比较 再替换
排序
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); } }}都是分为两种情况进行处理,支持随机访问的使用swap首尾交换
使用迭代器的需要两个迭代器,一个前向迭代 一个后向迭代
使用中间值进行交换 - shuffle——对List中的元素随机排列 类似Arrays.shuffle()方法
Collections.shuffle(List< ? > 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);}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); }}发现其实底层是调用的Arrays.sort()方法,然后调用迭代器给现在的list赋值。 Collections.reverseOrder(Comparator cmp)
返回一个和入参的 Comparator cmp比较器 反向的比较器 其他
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;}同样,对于链表和数组有不同的操作方式。 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]- 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]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; }显然这个方法 只能是实现了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());结果[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源码 其实是创建了一个新的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 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>());
【转载】原文地址:https://blog.csdn.net/u011863951/article/details/80725107
|
|