上一篇学习了List集合的存储方式,下面继续学习Set集合
Set : 元素是无序的(存入和取出的顺序不一定一致),元素是不可以重复的
Set集合的功能和Collection是一致的,接下来看一下常见的子类
|----HashSet:底层的结构是hash表.线程是非同步的
**HashSet是如何保证元素唯一性的呢?
是通过 *元素* 的两个方法hashCode和equals来完成。如果元素的HashCode值相等,才会判断equals是否为true,如何元素的Hash值不等,不会调用equals**。
**注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的HashCode和equals方法**
|----TreeSet:可以对set集合中的元素进行排序,底层数据结构是二叉树,保证元素唯一性的依据:compareTo方法return 0。
TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Comprable接口,覆盖compareTo方法,这种方式也成为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式,当元素自身不具备比较性时,或者具备的比较性不是所需要的,这是就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。
演示代码HashSet
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add("java1");
hashSet.add("java1");
hashSet.add("java2");
hashSet.add("java3");
hashSet.add("java4");
//也用迭代器取出
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()) {
sop(iterator.next());
}
}
元素是无序的取出且不重复:
java1
java3
java2
java4
练习
往hashSet集合中存入自定义对象,姓名和年龄相同视为同一个人,重复元素。
class Person{
int age;
String name;
public Person(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
//需要重写hash code,因为hash表会先判断每个对象的hash值是否相等
//我们需要让他们的hash值都相等的情况下,再去执行equal方法,进行内容的判断
@Override
public int hashCode() {
return 60;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person)) {
return false;
}
Person person = (Person)obj;
return this.name.equals(person.getName()) && this.age == person.getAge();
}
}
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
HashSet hashSet = new HashSet();
//person对象中的每一个元素,都会有自己的hash值
hashSet.add(new Person(11, "a1"));
hashSet.add(new Person(11, "a2"));
hashSet.add(new Person(11, "a3"));
hashSet.add(new Person(11, "a2"));
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()) {
Person person = (Person)iterator.next();
sop(person.getAge() + ".."+person.getName());
}
}
原理图:60 == 3c
运行结果:
11..a3
11..a2
11..a1
思考一下,如果不加HashCode和equals方法会打印什么呢?
结果如下:
11..a2
11..a1
11..a2
11..a3
因为它比较的是每个对象的Hash值,每个对象的Hash值是不一样的,所以会存入相同的元素。
真的上面的HashCode进行优化,可以减少判断的次数,没有必要写死,可以通过元素自身来定义Hash值,*39的目的是为了避免元素的Hash值相等,提高Hash值的唯一性,效率高。
@Override
@Override
public int hashCode() {
//每个字符串都有自己的hash值,使用这种方式来判断hash值是否相等
return name.hashCode()+age*39;
}
演示代码TreeSet
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add("cds");
treeSet.add("DD");
treeSet.add("vvff");
treeSet.add("aaas");
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
sop(iterator.next());
}
}
打印结果:可以看到它对元素进行了字母的排序
DD
aaas
cds
vvff
练习1,第一种排序方式
需求:往TreeSet集合中存储自定义对象人,想按照学生的年龄进行排序。
class Person{
int age;
String name;
public Person(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Person(11, "lisi"));
treeSet.add(new Person(21, "wang"));
treeSet.add(new Person(19, "zhangsan"));
treeSet.add(new Person(22, "xiaowang"));
while (iterator.hasNext()) {
Person person = (Person)iterator.next();
sop(person.getAge()+"..."+person.name);
}
}
}
编译运行出错:48行 对应treeSet.add(new Person(11, “lisi”));
Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at Demo.main(Demo.java:48)
Comparable是一个接口,此接口强行实现它的每个对象进行整体排序
我们再往Treeset存对象的时候,它会帮忙排序,但是Person不具备比较性,所以我们需要让Person具备比较性,需要实现Comparable接口
class Person implements Comparable{
int age;
String name;
public Person(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public int compareTo(Object o) {
if(!(o instanceof Person)){
throw new RuntimeException("不是Person对象");
}
Person person = (Person)o;
if (this.age > person.getAge()) {
return 1;
}else if (this.age == person.getAge()) {
return 0;
}
return -1;
}
}
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Person(11, "lisi"));
treeSet.add(new Person(21, "wang"));
treeSet.add(new Person(19, "zhangsan"));
treeSet.add(new Person(22, "xiaowang"));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
Person person = (Person)iterator.next();
sop(person.getAge()+"..."+person.name);
}
}
}
打印结果:
11...lisi
19...zhangsan
21...wang
22...xiaowang
如果元素有两个相同的年龄,还要比较一下name是否相同,优化compareTo方法如下:
@Override
public int compareTo(Object o) {
if(!(o instanceof Person)){
throw new RuntimeException("不是Person对象");
}
Person person = (Person)o;
if (this.age > person.getAge()) {
return 1;
}else if (this.age == person.getAge()) {
//String字符默认有compareTo的排序方法,按照自然排序
return this.name.compareTo(person.name);
}
return -1;
}
存入相同年龄不同名字。
TreeSet treeSet = new TreeSet();
treeSet.add(new Person(11, "lisi"));
treeSet.add(new Person(21, "wang"));
treeSet.add(new Person(21, "wang02"));
treeSet.add(new Person(19, "zhangsan"));
treeSet.add(new Person(22, "xiaowang"));
打印结果如下:
11...lisi
19...zhangsan
21...wang
21...wang02
22...xiaowang
练习2,第二种排序方式
当元素自身不具备比较性,或者具备的比较性不是所需要的,这是需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
class Person implements Comparable{
int age;
String name;
public Person(int age,String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public int compareTo(Object o) {
if(!(o instanceof Person)){
throw new RuntimeException("不是Person对象");
}
Person person = (Person)o;
if (this.age > person.getAge()) {
return 1;
}else if (this.age == person.getAge()) {
//String字符默认有compareTo的排序方法,按照自然排序
return this.name.compareTo(person.name);
}
return -1;
}
}
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
//传入自己的比较器,比Person的比较器优先级高
TreeSet treeSet = new TreeSet(new MyCompare());
treeSet.add(new Person(11, "lisi"));
treeSet.add(new Person(21, "wang"));
treeSet.add(new Person(21, "wang3"));
treeSet.add(new Person(19, "zhangsan"));
treeSet.add(new Person(22, "xiaowang"));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
Person person = (Person)iterator.next();
sop(person.getAge()+"..."+person.name);
}
}
}
class MyCompare implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Person person = (Person)o1;
Person person2 = (Person)o2;
int num = person.getName().compareTo(person2.getName());
if (num == 0) {
//如果name是相同的,就比较年龄
return new Integer(person.getAge()).compareTo(person2.getAge());
}
return num;
}
}
打印效果,按照name排序:
11...lisi
21...wang
21...wang3
22...xiaowang
19...zhangsan
练习3,第二种排序方式
需求:按照字符串长度进行排序。
字符串本身具备比较性,但是它的比较方式不是所需的,这时就只能使用比较器。
public class Demo {
public static void sop(Object object){
System.out.println(object);
}
public static void main(String[] args) {
TreeSet treeSet = new TreeSet(new MyCompare());
treeSet.add("dd");
treeSet.add("uu");
treeSet.add("asf");
treeSet.add("eeee");
treeSet.add("cvfss");
treeSet.add("bfxxt");
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()) {
sop(iterator.next());
}
}
}
class MyCompare implements Comparator{
@Override
public int compare(Object o1, Object o2) {
String s1 = (String)o1;
String s2 = (String)o2;
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if (num == 0) {
return s1.compareTo(s2);
}
return num;
}