深入研究容器Collections实用方法
【摘要】 java中有大量用于容器的卓越的使用方法,它们被表示为java.util.Collections类内部的静态方法。你已经看到过其中的一部分,例如addAll()、reverseOrder()和binarySearch()。下面是另外一部分(synchronized和unmodifiable的使用方法将在后续的小节中介绍)。在这张表中,在相关的情况中使用了泛型:checkedColle...
java中有大量用于容器的卓越的使用方法,它们被表示为java.util.Collections类内部的静态方法。你已经看到过其中的一部分,例如addAll()、reverseOrder()和binarySearch()。下面是另外一部分(synchronized和unmodifiable的使用方法将在后续的小节中介绍)。在这张表中,在相关的情况中使用了泛型:
checkedCollection(Collection<T>, Class<T> type) checkedList(List<T>, Class<T> type) checkedMap(Map<K, V>, Class<K> keyType, Class<V> valueType ) checkedSet(Set<T>, Class<T> type) checkedSortedMap(SortedMap<K, V>, Class<K> keyType, Class<V> valueType) checkedSortedSet(SortedSet<T>, Class<T> type) |
产生Collection或者Collection的具体子类型的动态类型安全的视图。在不可能使用静态检查版本时使用这些方法。这些方法在“动态类型安全”章节中进行过说明 |
max(Collection) min(Collection) |
返回参数Collection中最大或最小的元素——采用Collection内置的自然比较法 |
max(Collection, Comparator) min(Collection, Comparator) |
返回参数Collection中最大或最小的元素——采用Comparator进行比较 |
indexOfSubList(List source, List target) | 返回target在source中第一次出现的位置,或者在找不到时返回-1 |
lastIndexOfSubList(List source, List target) | 返回target在source中最后一次出现的位置,或者在找不到时返回-1 |
replaceAll(List<T>, T oldVal, T newVal) | 使用newVal替换所有的oldVal |
reverse(List) | 逆转所有元素的次序 |
reverseOrder() reverseOrder(Comparator<T>) |
返回一个Comparator,它可以逆转实现了Comparator<T>的对象集合的自然排序。第二个版本可以逆转所提供的Comparator的顺序。 |
rotate(List, int distance) | 所有元素向后移动distance个位置,将末尾的元素循环到前面来 |
shuffle(List) shuffle(List, Random) |
随机改变指定列表的顺序。第一种形式提供了其自己的随机机制,你可以通过第二种形式提供自己的随机机制 |
sort(List<T>) sort(List<T>, Comparator<? super T> c) |
使用List<T>中的自然顺序排序。第二种形式允许提供用于排序的Comparator |
copy(List<? super T> dest, List<? extends T> src) | 将src中的元素复制到dest |
swap(List, int i, int j) | 交换list中位置i与位置j的元素。通常比你自己写的代码快 |
fill(List<? super T>, T x) | 用对象x替换list中的所有元素 |
nCopies(int n, T x) | 返回大小为n的List<T>,此List不可改变,其中的引用都指向x |
disjoint(Collection, Collection) | 当两个集合没有任何相同元素时,返回true |
frequency(Collection, Object x) | 返回Collection中等于x的元素个数 |
emptyList() emptyMap() emptySet() |
返回不可改变的空List、Map或Set。这些方法都是泛型的,因此所产生的结果将被参数化为所希望的类型 |
singleton(T x) singletonList(T x) singletonMap(K key, V value) |
产生不可变的Set<T>、List<T>或Map<K, V>,它们都只包含基于所给定参数的内容而形成的单一项 |
list(Enumeration<T> e) | 产生一个ArrayList<T>,它包含的元素的顺序,与(旧式的)Enumeration(Iterator的前身)返回这些元素的顺序相同。用来转换遗留的老代码 |
enumeration(Collection<T>) | 为参数生成一个旧式的Enumeration<T> |
注意,min()和max()只能作用于Collection对象,而不能作用于List,所以你无需担心Collection是否应该被排序(如前所述,只有在执行binarySearch()之前,才确定需要对List或数组进行排序)。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;
import java.util.Vector;
public class Utilities {
static List<String> list = Arrays.asList("one Two three Four five six one".split(" "));
public static void main(String[] args) {
System.out.println(list);
System.out.println("list disjoint (Four)? : " + Collections.disjoint(list, Collections.singletonList("Four")));
System.out.println("max: " + Collections.max(list));
System.out.println("min: " + Collections.min(list));
System.out.println("max w/ comparator: " + Collections.max(list, String.CASE_INSENSITIVE_ORDER));
System.out.println("min w/ comparator: " + Collections.min(list, String.CASE_INSENSITIVE_ORDER));
List<String> sublist = Arrays.asList("Four five six".split(" "));
System.out.println("indexOfSubList: " + Collections.indexOfSubList(list, sublist));
System.out.println("lastIndexOfSubList: " + Collections.lastIndexOfSubList(list, sublist));
Collections.replaceAll(list, "one", "Yo");
System.out.println("replaceAll: " + list);
Collections.reverse(list);
System.out.println("reverse: " + list);
Collections.rotate(list, 3);
System.out.println("rotate: " + list);
List<String> source = Arrays.asList("in the matrix".split(" "));
Collections.copy(list, source);
System.out.println("copy: " + list);
Collections.swap(list, 0, list.size() - 1);
System.out.println("swap: " + list);
Collections.shuffle(list, new Random(47));
System.out.println("shuffled: " + list);
Collections.fill(list, "pop");
System.out.println("fill: " + list);
System.out.println("frequency of 'pop': " + Collections.frequency(list, "pop"));
List<String> dups = Collections.nCopies(3, "snap");
System.out.println("dups: " + dups);
System.out.println("'list' disjoint 'dups'? " + Collections.disjoint(list, dups));
Enumeration<String> e = Collections.enumeration(dups);
Vector<String> v = new Vector<String>();
while (e.hasMoreElements())
v.addElement(e.nextElement());
ArrayList<String> arrayList = Collections.list(v.elements());
System.out.println("arrayList: " + arrayList);
}
}
该程序的输出可看作是对每个实用方法的行为的解释。请注意由于大小写的缘故而造成的使用String.CASE_INSENSITIVE_ORDER Comparator时min()和max()的差异。
一、List的排序和查询
List排序与查询所使用的方法与对象数组所使用的相应方法有相同的名字与语法,只是用Collections的static方法代替Arrays的方法而已。下面是一个例子,用到了Utilities.java中的list数据:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
public class ListSortSearch {
public static void main(String[] args) {
List<String> list = new ArrayList<String>(Utilities.list);
list.addAll(Utilities.list);
System.out.println(list);
Collections.shuffle(list, new Random(47));
System.out.println("shuffle: " + list);
ListIterator<String> it = list.listIterator(10);
while (it.hasNext()) {
it.next();
it.remove();
}
System.out.println("Trimmed: " + list);
Collections.sort(list);
System.out.println("sorted: " + list);
String key = list.get(7);
int index = Collections.binarySearch(list, key);
System.out.println("Location of " + key + " is " + index + ", list.get(" + index + ") = " + list.get(index));
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
System.out.println("Case-insensitive sorted: " + list);
key = list.get(7);
Collections.binarySearch(list, key, String.CASE_INSENSITIVE_ORDER);
System.out.println("Location of " + key + " is " + index + ", list.get(" + index + ") = " + list.get(index));
}
}
与使用数组进行查找和排序一样,如果使用Comparator进行排序,那么binarySearch()必须使用相同的Comparator。
此程序还演示了Collections的Shuffle()方法,它用来打乱List的顺序。ListIterator是在被打乱的列表中的某个特定位置创建的,并用来移除从该位置到列表尾部的所有元素。
二、设定Collection或Map不可修改
创建一个只读的Collection或Map,有时可以带来某些方便。Collections类可以帮助达成此目的,它有一个方法,参数为原本的容器,返回值是容器的只读版本。此方法有大量变种,对应于Collection(如果你不能把Collection视为更具体的类型)、List、Set和Map。下例将说明如何正确生成各种只读容器:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import com.buba.util.Countries;
public class ReadOnly {
static Collection<String> data = new ArrayList<>(Countries.names(6));
public static void main(String[] args) {
Collection<String> c = Collections.unmodifiableCollection(new ArrayList<String>(data));
System.out.println(c);
// c.add("one"); java.lang.UnsupportedOperationException
List<String> a = Collections.unmodifiableList(new ArrayList<String>(data));
ListIterator<String> lit = a.listIterator();
System.out.println(lit.next());
// lit.add("one");
Set<String> s = Collections.unmodifiableSet(new HashSet<String>(data));
System.out.println(s);
// s.add("one");
Set<String> ss = Collections.unmodifiableSortedSet(new TreeSet<String>(data));
Map<String, String> m = Collections.unmodifiableMap(new HashMap<String, String>(Countries.capitals(6)));
System.out.println(m);
// m.put("Ralph", "Howdy!");
Map<String, String> tm = Collections.unmodifiableSortedMap(new TreeMap<String, String>(Countries.capitals(6)));
}
}
对特定类型的“不可修改的”方法的调用并不会产生编译时的检查,但是转换完成后,任何会改变容器内容的操作都会引起UnsupportedOperationException异常。
无论哪一种情况,在将容器设为只读之前,必须填入有意义的数据。装载数据后,就应该使用“不可修改的”方法返回的引用去替换掉原本的引用。这样,就不用担心无意中修改了只读的内容。另一方面,此方法允许你保留一份可修改的容器,作为类的private成员,然后通过某个方法调用返回对该容器的“只读”的引用。这样一来,就只有你可以修改容器的内容,而别人只能读取。
三、Collection或Map的同步控制
关键字synchronized是多线程议题中的重要部分,在并发中将讨论这种较为复杂的主题。这里,我只是提醒注意,Collections类有办法能够自动同步整个容器。其语法与“不可修改的”方法相似:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
public class Synchronization {
public static void main(String[] args) {
Collection<String> c = Collections.synchronizedCollection(new ArrayList<String>());
List<String> list = Collections.synchronizedList(new ArrayList<String>());
Set<String> s = Collections.synchronizedSet(new HashSet<String>());
Set<String> ss = Collections.synchronizedSortedSet(new TreeSet<String>());
Map<String, String> m = Collections.synchronizedMap(new HashMap<String, String>());
Map<String, String> sm = Collections.synchronizedSortedMap(new TreeMap<String, String>());
}
}
最好是如上所示,直接将新生成地容器传递给适当的“同步”方法;这样做就不会有任何机会暴露出不同步的版本。
四、快速报错
java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容。如果你在迭代遍历某个容器的过程中,另一个进程介入其中,并且插入、删除或修改此容器内的某个对象,那么就会出现问题:也许迭代过程已经处理过容器中该元素了,也许还没处理,也许在调用size()之后容器的尺寸收缩了——还有许多灾难情景。java容器类类库采用快速报错(fail-fast)机制。它会探查容器上的任何除了你的进程所进行的操作以外的所有变化,一旦它发现其他进程修改了容器,就会立刻抛出ConcurrentModificationException异常。这就是“快速报错”的意思——即,不是使用复杂的算法在事后来检查问题。
很容易就可以看出“快速报错”机制的工作原理:只需创建一个迭代器,然后向迭代器所指向的Collection添加点什么,就像这样:
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
public class FailFast {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
Iterator<String> it = c.iterator();
c.add("An object");
try {
String s = it.next();
} catch (ConcurrentModificationException e) {
System.out.println(e);
}
}
}
程序运行时发生了异常,因为在容器取得迭代器之后,又有东西被放入到了该容器中。当程序的不同部分修改同一个容器时,就可能导致容器的状态不一致,所以,此异常提醒你,应该修改代码。在此例中,应该在添加完所有的元素之后,再获取迭代器。
ConcurrentHashMap、CopyOnWriteArrayList和CopyOnWriteArraySet都使用了可以避免ConcurrentModificationException的技术。
如果本文对您有很大的帮助,还请点赞关注一下。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)