【JAVA】 容纳对象 Set

总概

a) 一个集合只接受对象每种类型中的一个。
b) HashSet能最快的查找元素
c) TreeSet按顺序排序元素
d) LinkedHashSet按插入顺序保持元素
e) 不能有重复的元素

Set

Iterface Set
public interface Set extends Collection

介绍
该集合不包含重复的元素。更正式地是,Set不包含一对满足e1.equals(e2)的元素e1和e2,并且最多只有一个null元素。

Set接口对于所有的构造方法和add, equals和 hashCode方法添加了额外的规定。

对构造方法的额外规定是,所有的构造器构造的Set不能包含重复的元素

注意:要十分注意可变的对象(mutable objects)用作Set元素时的情况。如果某个对象是Set的元素,且该对象被更改并影响了equals的比较(即可能出现e1.equals(e2)),则此时Set的行为不明确(not specified)。该禁止的一个特例是:不允许Set将自己作为一个元素包含进来。

一些Set实现对包含的元素有限制。例如,有些禁止null元素,有些对元素类型有限制。尝试添加不合要求的元素(ineligible)会抛出不确认的异常,通常是NullPointerException或者ClassCastException。试图查询不合要求元素的存在时可能会抛出异常,或者只是简单的返回false。更通常的情况是,尝试对不合格元素进行操作,如果完成操作不会导致不合格元素插入到集合中,则可能会引发异常,或者根据实现的选择(at the option of the implementation),可能会成功。在该接口的实现规范中这样的异常被标记为“可选”

主要方法

  1. int size
    返回set集合中的元素数量(其基数)。如果超过Integer.MAX_VALUE个元素,则返回Integer.MAX_VALUE
  2. boolean isEmpty()
    如果该set不包含元素,则返回true
  3. boolean contains(Object o)
    如果该set包含指定的元素在返回true。如果set包含的元素满足(0==null ? e==null : o.equals(e))则返回true
  4. Iterator iterator()
    返回该set元素的迭代器。返回的元素没有特定的顺序(除非set是某个提供了保障类的实例)
  5. Object[] toArray()
    返回一个包含该set所有元素的数组。如果该set保障了其迭代器该以什么顺序返回其元素,则该方法必须按相同的顺序返回元素。
  6. boolean add(E e)
    如果指定元素在该set中还不存在则将指定元素添加到该set中。如果该set不包含满足(e==null ? e2==null : e.equals(e2))的元素e,则将指定元素e添加到set中。如果该set已经包含元素,则不改变set且返回false。再结合对构造器的限制,确保set永远不包含重复的元素以上规范并不意味着set必须接受所有元素;sets可以拒绝添加特定的元素,包括null,然后抛出异常。单独的set实现应该清楚地说明对可能包含元素的限制。
  7. boolean remove(Object o)
    如果指定元素在该set中存在则从该set中移除指定的元素(可选操作)。如果该set包含满足(o==null ? e==null : o.equals(e))的元素e,则移除该元素。如果该set包含元素(等同的,如果该set在调用后发生了改变)则返回true。(一旦调用返回,则该set不再包含该元素)
  8. boolean containsAll(Collection<?> c)
    如果该set包含指定集合的所有元素,则返回true。如果指定结合也是set,且是该set的子set,则返回true
  9. boolean addAll(Collection<? extends E> c)
    如果指定集合的所有元素没有在该set中,则将该集合中的所有元素添加到该set中。(可选操作)。如果指定集合也是set,则addAll操作后得到的结果是两个set的和。如果指定集合在该操作进行中被修改,则该操作的行为未定义。
  10. boolean retainAll(Collection<?> c)
    只保留该set和指定集合共有的元素(可选操作)。如果指定的集合也是set,则该操作得到的值是两个set的交集
  11. boolean removeAll(Collection<?> c)
    移除该set所含有的所有指定集合的元素(可选操作)。如果指定的集合也是set,则该操作得到的是两个set的不对称集差(asymmetric set difference)
  12. void clear()
    移除该set的所有元素(可选操作)。set将为空。
  13. boolean equals(Object o)
    比较指定和集合和该set。如果指定的集合也是set,两个set同样的大小,指定set的每个成员元素都包含在该set中(或这个set的每个成员都包含在指定的set中)。该定义确保该方法在set接口的不同实现能正常工作
  14. int hashCode()
    返回该set的哈希码值。set的哈希代码定义为set元素的哈希码之和,其中空元素的哈希码定义为零。这样确保s1.equals(s2)意味着s1.hashCode()==s2.hashCode()

HashSet

Class HashSet
public class HashSet extends AbstractSet implements Set, Cloneable, Serializable

介绍
该类实现Set接口,由哈希表(实际上是HashMap的实例)支持。不保证set的迭代顺序。特别是,不能保证顺序在一段时间内保持不变。该类允许null元素

此类为基本操作(add、remove、contains和size)提供恒定时间性能(constant time performance),假设hash函数合理的分配元素。迭代此集合需要与哈希集实例的大小(元素数)加上支持哈希映射实例的“容量(capacity)”(存储桶数(the number of buckets))之和成比例的时间(Iterating over this set requires time proportional to the sum of the HashSet instance’s size (the number of elements) plus the “capacity” of the backing HashMap instance (the number of buckets).)。因此,如果要追求迭代的性能,注意不要把初始容量设置得太大也不要把负载系数(load factor)设置得太低

注意:此实现线程不同步。如果多个线程同时访问hash set,并且至少有一个线程在结构上修改了set,则必须在外部进行同步(synchronized externally)。通常通过自然封装了set来实现线程同步。如果不存在这样的对象,则要使用Collections.synchronizedSet方法“包装”列表。该操作最好在创建时完成,以防止不同步的访问意外地发生:
List list = Collections.synchronizedSet(new HashSet(…))

该类的iterator方法返回的迭代器是fail-fast。如果在迭代器创建后的任何时候对set进行结构性的修改,则除了迭代器自身的remove方法,其他所有方法都会导致迭代器抛出ConcurrentModificationException异常。因此,在出现并发修改的情况下,迭代器会快速地失败报错,从而避免了在将来未知的时候出现不确定的行为。

该类是Java集合框架中的一员。

方法
构造方法:

  1. public HashSet()
    构造新的空的set,其后支持的HashMap实例(backing HashMap instance)的默认初始容量(16)和负载系数(load factor)(0.75)
  2. public HashSet(Collection< ? extends E> c)
    构造一个新的set包含指定集合的元素。该HashSet的默认负载系数(0.75),初始容量则是足够容纳指定集合的元素
  3. public HashSet(int initialCapacity, float loadFactor)
    构造新的空set,其后支持的HashMap实例有指定的初始容量和指定的负载系数
  4. public HashSet(int initialCapacity)
    构造新的空set,其后支持的HashMap实例有指定的初始容量和默认的负载系数(0.75)

主要方法:

  1. public Iterator iterator()
    返回该set元素的迭代器。返回的元素没有特定的顺序。
  2. public int size()
    返回该set的元素数量(其基数)
  3. public boolean isEmpty()
    如果该set不含任何元素则返回true
  4. public boolean contains(Object o)
    如果该set包含特定元素则返回true。如果set包含的元素满足(0==null ? e==null : o.equals(e))则返回true。
  5. public boolean add(E e)
    如果指定元素在该set中还不存在则将指定元素添加到该set中。如果该set不包含满足(e==null ? e2==null : e.equals(e2))的元素e,则将指定元素e添加到set中。如果该set已经包含元素,则不改变set且返回false。再结合对构造器的限制,确保set永远不包含重复的元素
  6. public boolean remove(Object o)
    如果指定元素在该set中存在则从该set中移除指定的元素。如果该set包含满足(o==null ? e==null : o.equals(e))的元素e,则移除该元素。如果该set包含元素(等同的,如果该set在调用后发生了改变)则返回true。(一旦调用返回,则该set不再包含该元素)
  7. public void clear()
    移除该set的所有元素。set将为空。
  8. public Object clone()
    返回该HashSet实例的浅复制:元素自己不会复制

TreeSet

Class TreeSet
public class TreeSet
extends AbstractSet
implements NavigableSet, Cloneable, Serializable

介绍
NavigableSet实现是基于TreeMap。元素按照自然顺序排列,或者按照创建set时提供的比较器(Comparator)排列顺序,比较器取决于具体使用的构造方法。

此实现为基本操作(add, remove和contains)保障log(n)的时间成本。

注意,如果要正确实现Set接口,则由set维护的顺序(无论有没有明确提供Comparator)必须与equals保持一致(consistent with equals)。(“与equals保持一致”的精确定义,请参阅Comparable或Comparator)。这是因为Set接口是根据equals操作定义的,而TreeSet实例使用compareTo(或compare)方法来执行所有元素的比较。因此从set角度来看,通过这种方法被判断为相等的元素是相等的。即便其排序和equals不一致,set的行为也是被明确定义的;只是没有遵循Set接口的一般要求

注意:此实现线程不同步。如果多个线程同时访问tree set,并且至少有一个线程在结构上修改了set,则必须在外部进行同步(synchronized externally)。通常通过自然封装了set来实现线程同步。如果不存在这样的对象,则要使用Collections.synchronizedSortedSet方法“包装”列表。该操作最好在创建时完成,以防止不同步的访问意外地发生:
List list = Collections.synchronizedSortedSet(new TreeSet(…))

该类的iterator方法返回的迭代器是fail-fast。如果在迭代器创建后的任何时候对set进行结构性的修改,则除了迭代器自身的remove方法,其他所有方法都会导致迭代器抛出ConcurrentModificationException异常。因此,在出现并发修改的情况下,迭代器会快速地失败报错,从而避免了在将来未知的时候出现不确定的行为。

该类是Java集合框架中的一员。

方法
构造方法:

  1. public TreeSet()
    构造一个新的,空的tree set,并根据元素自然顺序排序。所有插入到该set的元素必须实现Comparable接口。更近一步说,所有的元素都是可以相互比较的(mutually comparable):对于set的元素e1和e2,e1.compareTo(e2)不准抛出ClassCastException。
    如果用户试图向set中添加元素而违反了限制(例如set的元素是int,用户添加的元素是string),则会抛出ClassCastException。
  2. public TreeSet(Comparator<? super E> comparator)
    构造一个新的,空的tree set,并根据元素自然顺序排序。所有的元素都是可以使用特殊comparator:comparator.compare(e1,e2)相互比较的:对于set的元素e1和e2,comparator.compare(e1,e2)不准抛出ClassCastException。
    如果用户试图向set中添加元素而违反了限制(例如set的元素是int,用户添加的元素是string),则会抛出ClassCastException。如果用户试图向set中添加元素而违反了限制,则会抛出ClassCastException。
  3. public TreeSet(Collection<? extends E> c)
    构造一个新的,包含指定集合元素的 tree set,并根据指定集合元素的自然顺序排序。所有插入到该set的元素必须实现Comparable接口。更近一步说,所有的元素都是可以相互比较的(mutually comparable):对于set的元素e1和e2,comparator.compare(e1,e2)不准抛出ClassCastException。
  4. public TreeSet(SortedSet s)
    构造一个新的tree set包含指定sorted set的相同元素并按相同顺序排列。

主要方法:

  1. public Iterator iterator()
    以升序返回此集合元素的迭代器。
  2. public Iterator descendingIterator()
    以降序返回此集合元素的迭代器。
  3. public int size()
  4. public boolean isEmpty()
  5. public boolean contains(Object o)
  6. public boolean add(E e)
  7. public boolean remove(Object o)
  8. public void clear()
  9. public boolean addAll(Collection<? extends E> c)
  10. public Object clone()
  11. public Spliterator spliterator()

总结

Set(接口) 添加到Set的每个元素都必须是独一无二的;否则Set就不会添加重复的元素。添加到Set里的对象必须定义equals(),从而建立对象的唯一性。Set拥有与Collection完全相同的接口。一个Set不能保证自己可按任何特定的顺序维持自己的元素
HashSet* 用于除非常小的以外的所有Set。对象也必须定义hashCode()
ArraySet 由一个数组后推得到的Set。面向非常小的Set设计,特别是那些需要频繁创建和删除的。对于小Set,与HashSet相比,ArraySet创建和反复所需付出的代价都要小得多。但随着Set的增大,它的性能也会大打折扣。不需要HashCode()
TreeSet 由一个“红黑树”后推得到的顺序Set(注释⑦)。这样一来,我们就可以从一个Set里提到一个顺序集合

例子

//: Set1.java
// Things you can do with Sets
package c08.newcollections;
import java.util.*;

public class Set1 {
  public static void testVisual(Set a) {
    Collection1.fill(a);
    Collection1.fill(a);
    Collection1.fill(a);
    Collection1.print(a); // No duplicates!
    // Add another set to this one:
    a.addAll(a);
    a.add("one"); 
    a.add("one"); 
    a.add("one");
    Collection1.print(a);
    // Look something up:
    System.out.println("a.contains(\"one\"): " +
      a.contains("one"));
  }
  public static void main(String[] args) {
    testVisual(new HashSet());
    testVisual(new TreeSet());
  }
} ///:~

重复的值被添加到Set,但在打印的时候,我们会发现Set只接受每个值的一个实例。
运行这个程序时,会注意到由HashSet维持的顺序与ArraySet是不同的。这是由于它们采用了不同的方法来保存元素,以便它们以后的定位。ArraySet保持着它们的顺序状态,而HashSet使用一个散列函数,这是特别为快速检索设计的)。创建自己的类型时,一定要注意Set需要通过一种方式来维持一种存储顺序,就象本章早些时候展示的“groundhog”(土拔鼠)例子那样。下面是一个例子:

//: Set2.java
// Putting your own type in a Set
package c08.newcollections;
import java.util.*;

class MyType implements Comparable {
  private int i;
  public MyType(int n) { i = n; }
  public boolean equals(Object o) {
    return 
      (o instanceof MyType) 
      && (i == ((MyType)o).i);
  }
  public int hashCode() { return i; }
  public String toString() { return i + " "; }
  public int compareTo(Object o) {
    int i2 = ((MyType) o).i;
    return (i2 < i ? -1 : (i2 == i ? 0 : 1));
  }
}

public class Set2 {
  public static Set fill(Set a, int size) {
    for(int i = 0; i < size; i++)
      a.add(new MyType(i));
    return a;
  }
  public static Set fill(Set a) {
    return fill(a, 10);
  }
  public static void test(Set a) {
    fill(a);
    fill(a); // Try to add duplicates
    fill(a);
    a.addAll(fill(new TreeSet()));
    System.out.println(a);
  }
  public static void main(String[] args) {
    test(new HashSet());
    test(new TreeSet());
  }
} ///:~

对equals()及hashCode()的定义遵照“groundhog”例子已经给出的形式。在两种情况下都必须定义一个equals()。但只有要把类置入一个HashSet的前提下,才有必要使用hashCode()——这种情况是完全有可能的,因为通常应先选择作为一个Set实现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值