Set
元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
HashSet
底层数据结构是哈希表。是线程不安全的。线程是非同步的。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
Set集合的功能和Collection是一致的。
import java.util.*;
class HashSetDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
HashSet hs = new HashSet();
sop(hs.add("java01"));
sop(hs.add("java01"));
hs.add("java02");
hs.add("java03");
hs.add("java03");
hs.add("java04");
Iterator it = hs.iterator();
while(it.hasNext())
{
sop(it.next());
}
}
}
true
false
java04
java03
java02
java01
/*
往hashSet集合中存入自定义对象。姓名和年龄相同为同一个人。
*/
import java.util.*;
class HashSetTest
{
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));
//sop("a1:"+hs.contains(new Person("a2",12)));
//hs.remove(new Person("a3",13));
Iterator it = hs.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName()+"......"+p.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return 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 int hashCode()
{
System.out.println(this.name+"......hashCode");
return name.hashCode()+age*37; //尽量保证hash值不一样;
}
}
结果:
a1……hashCode
a2……hashCode
a3……hashCode
a2……hashCode
a2…equals…a2
a3……13
a1……11
a2……12
首先我们要明确HashSet在进行equals之前要先进行hashCode的判断,这点和ArrayList不同,ArrayList只是调用equals方法。
因为一开始a1,a2,a3的哈希值不一样,只调用了hashCode方法,所以直接输出。当再次出现a2时,除了调用hashCode外,还要比较equals方法。
当加入
sop("a1:"+hs.contains(new Person("a2",12)));
hs.remove(new Person("a3",13));
我们发现还是会先调用hashCode,然后相同就会调用equals方法。
TreeSet
可以对Set集合中的元素进行排序
底层数据结构是二叉树
保证元素唯一性的依据
comparaTo方法return 0.
TreeSet排序的第一种方式
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet排序的第二种方式
当元素自身不具比较性或者具备的比较性不是所必需的这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。将比较器对象作为参数传递给TreeSet集合的构造函数.
当两种排序都存在时,以比较器为主。定义一个类,实现Comparator接口,覆盖compare方法。
第一种方式举例:
lass TreeSetDemo
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
ts.add("cba");
ts.add("aaa");
ts.add("bca");
ts.add("abcd");
Iterator it = ts.iterator();
while(it.hasNext())
sop(it.next());
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
输出结果:按照ASCII码大小排序
aaa
abcd
bca
cba
class TreeSetDemo
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
ts.add(new Student("lisi02",22));
ts.add(new Student("lisi03",20));
ts.add(new Student("lisi06",19));
Iterator it = ts.iterator();
while(it.hasNext())
{
Student stu = (Student)it.next();
sop(stu.getName()+"......"+stu.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class Student implements Comparable //该接口强制让学生具备比较性
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
public int compareTo(Object obj)
{
return 1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
结果:
lisi02……22
lisi03……20
lisi06……19
public int compareTo(Object obj);
返回1时,添加右子树节点
返回-1时,添加左子树节点
返回0时,就只是根节点,只剩一个元素,因为相等。
第二种方式举例:
import java.util.*;
class TreeSetDemo2
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student("lisi02",22));
ts.add(new Student("lisi03",20));
ts.add(new Student("lisi06",19));
ts.add(new Student("lisi03",21));
Iterator it = ts.iterator();
while(it.hasNext())
{
Student stu = (Student)it.next();
sop(stu.getName()+"......"+stu.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class Student implements Comparable //该接口强制让学生具备比较性
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("不是学生类");
Student stu = (Student)obj; //System.out.println(this.name+"...compareTo..."+stu.name);
if(this.age > stu.age)
return 1;
if(this.age == stu.age)
{
return this.name.compareTo(stu.name);
}
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class MyCompare implements Comparator
{
public int compare(Object o1,Object o2)
{
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
int num= stu1.getName().compareTo(stu2.getName());
if(num==0)
{
return new Integer(stu1.getAge()).compareTo (new Integer(stu2.getAge()));
}
/*
if(num==0)
{
if(stu1.getAge()>stu2.getAge())
return 1;
if(stu1.getAge()==stu2.getAge())
return 0;
return -1;
}
*/
return num;
}
}
因为使用第一种方式(让元素自身具备比较性,这是我们让Student类实现了Comparable接口),在类内部覆盖了compareTo方法,这时候是按照自然顺序排的。
这时候我们有了新的需求,就是要按照姓名排序,这时候咱们不能修改原代码吧,这不符合设计啊。
虽然Student本身具备了比较性但是不是我们所需要的,这时候新定义一个类,实现Comparator接口,并覆盖public int compare(Object o1,Object o2)方法;
public int compare(Object o1,Object o2)
{
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
int num= stu1.getName().compareTo(stu2.getName());
if(num==0)
{
return new Integer(stu1.getAge()).compareTo (new Integer(stu2.getAge()));
}
return num;
}
首先把年龄(整型)转换为Integer类,调用该类的compareTo方法比较年龄。返回的也是0,-1,1;
结果:
lisi02......22
lisi03......20
lisi03......21
lisi06......19