目录
HashSet
TreeSet
集合框架中的Set类:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
HashSet
特点:
- 底层数据结构是哈希表。是线程不安全的。不同步。
- HashSet是如何保证元素唯一性的呢? 是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。- 对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
例1:简单应用
class CollectionDemo {
public static void main(String[] args) {
HashSet hs=new HashSet();
//注意:add()返回的是boolean
System.out.println(hs.add("java1"));
System.out.println(hs.add("java1"));
hs.add("java2");
hs.add("java3");
hs.add("java3");
hs.add("java4");
Iterator it=hs.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
例2:往hashSet集合中存入自定对象姓名和年龄相同为同一个人,重复元素。
class CollectionDemo {
public static void main(String[] args) {
HashSet hs=new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a2",12));
Iterator it=hs.iterator();
while(it.hasNext())
{
Person p=(Person)it.next();
System.out.println(p.getName()+"..."+p.getAge());
}
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
//重写hasCode
public int hashCode()
{
System.out.println(this.name+"....hashCode");
return name.hashCode()+age;// 保证哈希值的唯一性
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
从运算结果可以看出来,每次添加进来的元素先计算hashCode,当添加进a2时由于hashCode一样,再比为较是否为同一个对象,调用equals方法。
例3:HashSet对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法
class CollectionDemo {
public static void main(String[] args) {
HashSet hs=new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a2",12));
System.out.println(hs.contains(new Person("a2",12))); //包含时,先判断hashCode,在判断equals
System.out.println(hs.remove(new Person("a1",11))); //删除时,先判断hashCode,在判断equals
Iterator it=hs.iterator();
while(it.hasNext())
{
Person p=(Person)it.next();
System.out.println(p.getName()+"..."+p.getAge());
}
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
//重写hasCode
public int hashCode()
{
System.out.println(this.name+"....hashCode");
return name.hashCode()+age*37;// 保证哈希值的唯一性37是任意值
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
TreeSet
- 可以对Set集合中的元素进行排序 ;
- 底层数据结构是二叉树;
- 保证元素唯一性的依据:compareTo方法return 0
TreeSet排序的第一种方式:让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。
这种方式也称为元素的自然顺序,或者叫做默认顺序。TreeSet的第二种排序方式。
当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。
例1:往TreeSet集合中存储自定义对象学生。想按照学生的年龄进行排序 。
class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts=new TreeSet();
ts.add(new Student("Mike",10));
ts.add(new Student("Tom",12));
ts.add(new Student("Mi",17));
ts.add(new Student("Jim",10));
Iterator it=ts.iterator();
while(it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
class Student implements Comparable//该接口强制让学生具备比较性。
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//覆盖compareTo方法。
public int compareTo(Object obj)
{
//return 0; //所有元素相同
if(!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student s = (Student)obj;
//System.out.println(this.name+"....compareto....."+s.name);
//根据年龄进行排序
if(this.age>s.age)
return 1;
if(this.age==s.age) //主要条件相同,比较次要条件姓名
{
return this.name.compareTo(s.name); //此处的compareTo为String中的比较方法,按字典顺序比较两个字符串
}
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
例2:当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。当两种排序都存在时,以比较器为主。
定义一个类,实现Comparator接口,覆盖compare方法。
//按照比较器进行排序
class CollectionDemo {
public static void main(String[] args) {
TreeSet ts=new TreeSet(new MyCompare());
ts.add(new Student("Mike",10));
ts.add(new Student("Tom",12));
ts.add(new Student("Mi",17));
ts.add(new Student("Aim",10));
Iterator it=ts.iterator();
while(it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
class Student implements Comparable//该接口强制让学生具备比较性。
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//覆盖compareTo方法。
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("不是学生对象");
Student s = (Student)obj;
//System.out.println(this.name+"....compareto....."+s.name);
//根据年龄进行排序
if(this.age>s.age)
return 1;
if(this.age==s.age) //主要条件相同,比较次要条件姓名
{
return this.name.compareTo(s.name); //此处的compareTo为String中的比较方法,按字典顺序比较两个字符串
}
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class MyCompare implements Comparator
{
public int compare(Object o1,Object o2)
{
Student s1 = (Student)o1;
Student s2 = (Student)o2;
int num = s1.getName().compareTo(s2.getName()); //调用String中的compareTo方法,按字典进行排序
//主要条件相同,判断次要条件
if(num==0)
{
if(s1.getAge()>s2.getAge())
return 1;
if(s1.getAge()==s2.getAge())
return 0;
return -1;
}
return num;
}
}
例3:按照字符串长度排序。(使用比较器排序)
class CollectionDemo {
public static void main(String[] args) {
TreeSet ts=new TreeSet(new StrLenComparator()); //将比较器对象作为参数传入集合构造函数中
ts.add("as");
ts.add("adf");
ts.add("dert");
ts.add("ed");
Iterator it=ts.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
//实现Comparator接口
class StrLenComparator implements Comparator
{
//复写compare方法
public int compare(Object o1,Object o2)
{
String s1=(String)o1; //向下转
String s2=(String)o2;
if(s1.length()>s2.length())
return 1;
if(s1.length()==s2.length())
return s1.compareTo(s2);
return -1;
}
}
注意:使用比较器时,将比较器对象作为参数传入集合构造函数中。