java中的类集之Set
java容器中有三个接口:Iterator、collection、map这三个接口是java所有容器类的最大父接口。
collection的子接口主要有list、set、queue;
list的实现类主要有:ArrayList、LinkedList、Vector、Stack
set的实现类主要有:HashSet、TreeSet、LinkedHashSet
queue的主要实现类有:PriorityQueue
Map接口的主要实现类有:HashMap、HashTable、TreeMap
1.Iterator接口的用法:
Iterator接口主要用来遍历Collection集合中的元素,Iterator对象也被称为迭代器。Iterator接口隐藏了各种Collecton实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口。当使用Iterator仅仅用于collection集合的输出,它没有盛装对象的能力,因此不能利用它来操作collection对象。
输出集合元素的代码(Iterator、foreache两种方式):
package com.shine.container;
import java.util.*;
public class IteratorDemo_01 {
public static void main(String[] args){
Collection books = new HashSet();
books.add("语文");
books.add("数学");
books.add("英语");
//用Iterator输出集合
Iterator it = books.iterator();
while(it.hasNext()){
String book = (String)it.next();
if(book.equals("语文")){
it.remove(); //a
//books.remove(book);//把a语句换成该语句会报错
}
book="测试字符串";
}
System.out.println(books);
//使用foreach输出集合
for(Object obj : books){
String book = (String)obj;
System.out.println(book);
if(book.equals("数学"))
books.remove(book);
}
System.out.println(books);
}
}
2.Set和HashSet
Set:Set集合中不允许有重复元素。set集合判断重复元素是根据equals()方法来判断的,只要equals方法
的返回值是false都能够成功的添加到set集合中,如果结果返回的是true则会抛出异常且添加失败。
HashSet:HashSet中的元素不能保证其排列顺序,顺序有可能发生变化。HashSet不是同步的,如果多个
线程同时访问一个HashSet则必须通过代码来保证其同步。HashSet判断两个元素是否相等是通过
equals方法返回ture并且两个对象的hashCode()方法的返回值也相同。
测试代码:
package com.shine.container;
import java.util.HashSet;
//person类的equals方法的返回值为true,没有重写hashCode方法
class Person{
public boolean equals(Object obj){
return true;
}
}
//person2类的hashCode方法的返回值为1,没有重写equals方法
class Person2{
public int hashCode(){
return 1;
}
}
//类person3的hashCode方法的返回值为2,并且重写了equals方法
class Person3{
public boolean equals(Object obj){
return true;
}
public int hashCode(){
return 2;
}
}
public class HashSetDemo_01 {
public static void main(String[] args) {
HashSet persons = new HashSet();
persons.add(new Person());
persons.add(new Person());
persons.add(new Person2());
persons.add(new Person2());
persons.add(new Person3());
persons.add(new Person3());
System.out.println(persons);
}
}
运行结果:[[email protected], [email protected],
[email protected], [email protected],
只有person3的第二个对象没有添加进去。
所以,当我们把一个对象放到HashSet中时,应该重写这个对象实现类的equals和hashCode方法。
hashCode()方法的重要性:当程序向HashSet集合中添加元素时,HashSet会根据该元素的hashCode()值
来计算它的存储位置。也就是说,每个元素的hashCode()值可以决定它的存储索引。HashSet采用元素的
hashCode()值来计算其索引,从而可以自由增加HashSet的长度,并可以根据元素的hashCode()值来访问
元素。这也是HashSet速度快的原因。
LinkedHashSet:
LinkedHashSet是HashSet的一个子类,它也是根据元素的HashSet值来决定元素的存储位置的,它同
时使用链表来维护元素的顺序。
LinkedHashSet记录了元素的添加顺序,它依然不允许内部有重复元素。
TreeSet
TreeSet是sortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
TreeSet采用红黑树的数据结构来存储 集合元素。TreeSet支持两种 排序方法:自然排序和定制排序。
1.自然排序:
TreeSet会调用集合元素的compareTo(0bject obj)方法来比较元素之间的大小关系,然后将集合元
素按升序排列,这种方式就是自然排序。
java中有一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一
个整数值,实现该接口的类必须实现该方法,实现了该接口的对象就可以比价大小。java中的一些常
用类已经实现了Comparable接口,并且提供了比较大小的标准。下面是这些常用类:
BigDecimal、BigInteger以及所有的数值型对应的包装类:按他们对应的数值大小进行比较。
Character:按字符的UNICODE值进行比较。
Boolean:true对应的包装类实例大于false对应的包装类实例。
String:按字符串中的UNICODE值进行比较。
Date、Time:后面的时间、日期比前面的时间、日期大。
如果试图把一个对象添加到TreeSet中,则该对象必须实现Comparable接口,否则程序将会抛出异
常。
2.定制排序:
TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,
则需要Comparator接口的帮助。该接口里有一个int compare(T o1,T o2)方法。
如果需要定制排序,则需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合
关联,由该Comaprator对象负责集合元素的排序逻辑。
当通过Comparator对象来实现TreeSet的定制排序时,依然不可以向TreeSet中添加类型不同的对
象,否则会引发ClassCastException异常。使用定制排序时,TreeSet对集合元素排序不管集合元素本身
的大小,而是由Comparator对象负责集合元素的排序规则。TreeSet判断两个元素相等的标准是:通过
Comparator比较两个元素返回了0,这样TreeSet不会把第二个元素添加到集合中。
总结:
HashSet的性能总是比TreeSet好,因为TreeSet需要额外的红黑树来维护集合元素的次序。只有当需要一个排序的Set时才使用TreeSet。
对于HashSet的子类LinkedHashSet普通的插入删除操作,LinkedHashSet的速度比HashSet慢,这是由于维护链表的额外开销造成的。不过,因为有了链表,遍历LinkedHashSet会更快。
EnumSet是所有Set实现类中最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
Set的三个实现类HashSet、TreeSet和EnumSet都不是线程安全的。通常可以通过Collections工具类的synchronizedSortedSet方法“包装"该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。