集合框架之Collections静态工具类

时间:2023-02-24 23:39:56


Collections类提供了一些列静态的方法,用以更方便地操作集合类

排序机制

一个List可以通过下面的方法进行排序:

Collections.sort(list);

如果List包含的是字符串,将会按照字母表排序;如果List包含的是Date类型数据,会按照日期先后排序……这是怎么实现的呢?String和Date都实现了comparable接口,此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo 方法被称为它的自然比较方法

实现此接口的对象列表(和数组)可以通过Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

对于类 C 的每一个 e1 和 e2 来说,当且仅当e1.compareTo(e2) == 0 与e1.equals(e2) 具有相同的 boolean值时,类 C 的自然排序才叫做与 equals 一致。注意,null 不是任何类的实例,即使e.equals(null) 返回 false,e.compareTo(null) 也将抛出 NullPointerException。

 

建议(虽然不是必需的)最好使自然排序与equals 一致。这是因为在使用自然排序与 equals 不一致的元素(或键)时,没有显式比较器的有序集合(和有序映射表)行为表现“怪异”。尤其是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。

例如,如果将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操作将返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是相等的。

实际上,所有实现 Comparable 的 Java 核心类都具有与equals 一致的自然排序。java.math.BigDecimal 是个例外,它的自然排序将值相等但精确度不同的 BigDecimal 对象(比如 4.0 和 4.00)视为相等。

如果试图对一个没有实现comparable接口的类进行排序,会抛出ClassCastException

 

Comparable接口包含下面的方法:

public interface Comparable<T> {
public int compareTo(T o);
}

compareTo方法比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

实现类必须确保对于所有的 x 和 y 都存在sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 的关系。(这意味着如果 y.compareTo(x)抛出一个异常,则 x.compareTo(y) 也要抛出一个异常。)

实现类还必须确保关系是可传递的:(x.compareTo(y)>0&& y.compareTo(z)>0) 意味着 x.compareTo(z)>0。

最后,实现者必须确保x.compareTo(y)==0 意味着对于所有的 z,都存在 sgn(x.compareTo(z)) == sgn(y.compareTo(z))。 强烈推荐(x.compareTo(y)==0) == (x.equals(y)) 这种做法,但并不是严格要求这样做。

在前面的描述中,符号sgn(expression) 指定 signum 数学函数,该函数根据 expression 的值是负数、零还是正数,分别返回 -1、0 或 1 中的一个值。

下面来看一个实例:

 

public class Name implements Comparable<Name> {
private final String firstName, lastName;

public Name(String firstName, StringlastName) {
if (firstName == null || lastName ==null)
throw new NullPointerException();
this.firstName = firstName;
this.lastName = lastName;
}

public String firstName() { return firstName;}
public String lastName() { return lastName; }

//覆写equals方法
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name) o;
return n.firstName.equals(firstName)&& n.lastName.equals(lastName);
}

//覆写了equals方法则必须覆写hashCode方法,相同的类必须有相同的哈希码
public int hashCode(){
return 31*firstName.hashCode() +lastName.hashCode();
}

public String toString() {
return firstName + " " + lastName;
}

//当姓氏和名字都一样时,才认为该类相同
public int compareTo(Name n) {
int lastCmp =lastName.compareTo(n.lastName);
//先比较姓氏,如果姓氏相同,再比较名字
return (lastCmp != 0 ? lastCmp :firstName.compareTo(n.firstName));
}
}

测试类
Name nameArray []= {
new Name("John","Smith"),
new Name("Karl","Ng"),
new Name("Jeff","Smith"),
new Name("Tom","Rich")
};

List<Name> names =Arrays.asList(nameArray);
Collections.sort(names);
System.out.println(names);

 

输出:

[Karl Ng, TomRich, Jeff Smith, John Smith]

 

如果我们不想按照自然排序,而是希望以一种特殊的方式排序呢?这是就需要提供一个Comparator。跟Comparable接口一样,该接口也只包含一个方法:

public interface Comparator<T> {
int compare(T o1, T o2);
}

Comparator提供了强行对某个对象collection 进行整体排序的比较函数。可以将 Comparator传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。

当使用具有与 equals 不一致的强行排序能力的Comparator 对有序 set(或有序映射)进行排序时,应该小心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一起使用。如果 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤其是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。

例如,假定使用 Comparator c 将满足(a.equals(b) && c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空TreeSet 中,则第二个 add 操作将返回 true(树 set 的大小将会增加),因为从树 set 的角度来看,a 和 b 是不相等的,即使这与 Set.add 方法的规范相反。

来看一个实例:

public class Employee implements Comparable<Employee> {
public Name name() { ... }
public int number() { ... }
public Date hireDate() { ... }
...
}

员工类包含员工的姓名、工号、入职日期等属性,并且实现了Comparable接口,它的自然排序是按照Name来排序的(具体代码见上面的Name类)。假如现在我们不想让它按照Name来排序,而是按照入职日期来排序,利用Comparator来实现:

  static final Comparator<Employee> SENIORITY_ORDER =
new Comparator<Employee>() {
public int compare(Employee e1,Employee e2) {
//根据入职日期进行排序,本质上还是利用了Date类型的compareTo方法
return e2.hireDate().compareTo(e1.hireDate());
}
};

// Employee database
static final Collection<Employee>employees = ... ;

public static void main(String[] args) {
List<Employee> e = new ArrayList<Employee>(employees);
Collections.sort(e, SENIORITY_ORDER); //传入指定的比较器
System.out.println(e);
}

同步“包装”机制

通用的集合类都是线程不安全的,如果需要线程安全的集合类,可以通过Collections类的包装对它们进行转换。

六个核心的接口都有一个静态的工厂方法:

public static <T> Collection<T>synchronizedCollection(Collection<T> c);

public static <T> Set<T> synchronizedSet(Set<T>s);

public static <T> List<T> synchronizedList(List<T>list);

public static <K,V> Map<K,V>synchronizedMap(Map<K,V> m);

public static <T> SortedSet<T>synchronizedSortedSet(SortedSet<T> s);

public static <K,V> SortedMap<K,V>synchronizedSortedMap(SortedMap<K,V> m);

例如,通过下面的这行语句:

List<Type>list = Collections.synchronizedList(new ArrayList<Type>());

我们就得到了一个线程安全的List

 

在多线程的情况下,使用者在进行迭代的时候需要注意集合的同步状态,推荐的做法是这样:

Collection<Type> c = Collections.synchronizedCollection(myCollection);
synchronized(c){
for (Type e : c)
foo(e);
}

如果使用的是显式迭代器,Iterator方法必须在同步快中调用。对Map的视图进行迭代时也是同样的要求。

Map<KeyType,ValType> m = Collections.synchronizedMap(new HashMap<KeyType,ValType>());
...
Set<KeyType> s = m.keySet();
...
// Synchronizingon m, not s!
synchronized(m){
while (KeyType k : s)
foo(k);
}

方法列表

static <T>boolean addAll(Collection<? superT> c, T... elements)

          将所有指定元素添加到指定collection 中。

static <T>int binarySearch(List<? extendsComparable<? super T>> list, T key)

          使用二分搜索法搜索指定列表,以获得指定对象。

static <E>Collection<E> checkedCollection(Collection<E>c, Class<E> type)

          返回指定 collection 的一个动态类型安全视图。

static <E>List<E> checkedList(List<E>list, Class<E> type)

          返回指定列表的一个动态类型安全视图。

static<K,V> Map<K,V> checkedMap(Map<K,V>m, Class<K> keyType, Class<V> valueType)

          返回指定映射的一个动态类型安全视图。

static <E>Set<E> checkedSet(Set<E>s, Class<E> type)

          返回指定 set 的一个动态类型安全视图。

static<K,V> SortedMap<K,V> checkedSortedMap(SortedMap<K,V>m, Class<K> keyType, Class<V> valueType)

          返回指定有序映射的一个动态类型安全视图。

static <E>SortedSet<E> checkedSortedSet(SortedSet<E>s, Class<E> type)

          返回指定有序 set 的一个动态类型安全视图。

static <T>void copy(List<? super T>dest, List<? extends T> src)

          将所有元素从一个列表复制到另一个列表。

static boolean disjoint(Collection<?> c1,Collection<?> c2)

          如果两个指定 collection 中没有相同的元素,则返回true。

static <T>List<T> emptyList()

          返回空的列表(不可变的)。

static<K,V> Map<K,V> emptyMap()

          返回空的映射(不可变的)。

static <T>Set<T> emptySet()

          返回空的 set(不可变的)。

static <T>Enumeration<T> enumeration(Collection<T>c)

          返回一个指定 collection 上的枚举。

static <T>void fill(List<? super T>list, T obj)

          使用指定元素替换指定列表中的所有元素。

static int frequency(Collection<?> c, Objecto)

          返回指定 collection 中等于指定对象的元素数。

static int indexOfSubList(List<?> source,List<?> target)

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

static int lastIndexOfSubList(List<?>source, List<?> target)

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

static <T>ArrayList<T> list(Enumeration<T>e)

          返回一个数组列表,它按返回顺序包含指定枚举返回的元素。

static <Textends Object & Comparable<? super T>> T max(Collection<?extends T> coll)

          根据元素的自然顺序,返回给定collection 的最大元素。

static <Textends Object & Comparable<? super T>> Tmin(Collection<? extends T> coll)

          根据元素的自然顺序 返回给定 collection 的最小元素。

static <T>List<T>  nCopies(int n, T o)

          返回由指定对象的 n 个副本组成的不可变列表。

static <T>boolean  replaceAll(List<T> list, T oldVal, T newVal)

          使用另一个值替换列表中出现的所有某一指定值。

static void reverse(List<?> list)

          反转指定列表中元素的顺序。 

static void rotate(List<?> list, int distance)

          根据指定的距离轮换指定列表中的元素。

static void shuffle(List<?> list)

          使用默认随机源对指定列表进行置换。 

static <T>Set<T> singleton(T o)

          返回一个只包含指定对象的不可变 set。

static <T>List<T> singletonList(T o)

          返回一个只包含指定对象的不可变列表。

static<K,V> Map<K,V> singletonMap(Kkey, V value)

          返回一个不可变的映射,它只将指定键映射到指定值。

static <Textends Comparable<? super T>> voidsort(List<T> list)

          根据元素的自然顺序 对指定列表按升序进行排序。

static void swap(List<?> list, int i, int j)

          在指定列表的指定位置处交换元素。

static <T>Collection<T> synchronizedCollection(Collection<T>c)

          返回指定 collection 支持的同步(线程安全的)collection。

static <T>List<T> synchronizedList(List<T>list)

          返回指定列表支持的同步(线程安全的)列表。

static<K,V> Map<K,V> synchronizedMap(Map<K,V>m)

          返回由指定映射支持的同步(线程安全的)映射。

static <T>Set<T> synchronizedSet(Set<T>s)

          返回指定 set 支持的同步(线程安全的)set。

static<K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V>m)

          返回指定有序映射支持的同步(线程安全的)有序映射。

static <T>SortedSet<T> synchronizedSortedSet(SortedSet<T>s)

          返回指定有序 set 支持的同步(线程安全的)有序 set。

static <T>Collection<T> unmodifiableCollection(Collection<?extends T> c)

          返回指定 collection 的不可修改视图。

static <T>List<T> unmodifiableList(List<?extends T> list)

          返回指定列表的不可修改视图。

static<K,V> Map<K,V> unmodifiableMap(Map<?extends K,? extends V> m)

          返回指定映射的不可修改视图。

static <T>Set<T> unmodifiableSet(Set<?extends T> s)

          返回指定 set 的不可修改视图。

static<K,V> SortedMap<K,V> unmodifiableSortedMap(SortedMap<K,?extends V> m)

          返回指定有序映射的不可修改视图。

static <T>SortedSet<T> unmodifiableSortedSet(SortedSet<T>s)

          返回指定有序 set 的不可修改视图。