Set集合与Lis集合有许多区别,Set集合没有提供Collection接口额外的方法,
List的元素允许重复,而Set的元素则不允许重复,添加相同元素时Set集合会覆盖。
Set集合的常用类:
HashSet:散列存放(重点)
TreeSet:有序存放(重点)
LinkedHashSet: 有次序
一丶
HashSet:散列存放(重点)
-
是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
-
当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
-
HashSet的其他操作都是基于HashMap的。
public static void main(String[] args) {
Set set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c");//添加元素
set.add("c");//添加相同元素会覆盖
System.out.println(set);
}
HashSet也提供了许多增删查改的方法,如果想添加相同元素,就需要重写HashCode.
2.2 hashCode()和equals()
因为hashCode()和equals()方法的返回值共同决定了两个对象是否相等,所以覆写着两个方法时一般要保证两个方法的
返回值保证兼容。
重写hashCode()和equals()方法的基本规则:
1、 如果两个对象通过equals()方法比较时返回true,则两个对象的hashCode()方法返回值应该也相等。
2、 对象中用作equals()比较标准的成员变量(属性),也应该参与到hashCode的计算。
二丶
TreeSet:有序存放(重点)
TreeSet是可排序的集合,TreeSet使用红黑树结构对加入的元素进行排序存放,所以放入TreeSet中元素必须是可
“排序”
的。
TreeSet可是采用两种方法实现排序:自然排序和定制排序。默认情况,TreeSet采用自然排序。
TreeSet调用调用集合元素的CompareTo()方法,根据该方法的返回值来比较元素之间的大小,然后进行“升序”排列,这种排序方式我们称之为自然排列。
注意:如果想采用自然排序,则要存储的对象所属类必须实现Comparable 接口。该接口只有一个方法public int compareTo(Object obj),必须实现该方法。
compareTo方法的实现规则:
返回 0,表示 this == obj。//则不会添加新对象
返回正数,表示 this> obj //添加到原来对象的右边
返回负数,表示 this < obj // 添加到原来对的左边
package Demo02;
import java.io.Serializable;
public class Student implements Comparable<Student>, Serializable {
/**
* 学号,姓名,年龄信息
*/
private int number;
private String name;
private int age;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(int number, String name, int age) {
this.number = number;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"number=" + number +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//根据年龄降序序排序
int rs = o.age - this.age;
if(rs == 0 ){
if(o == this){
return 0;
}else{
return -1;
}
}
return rs;
}
}
TreeSet treeSet = new TreeSet();
treeSet.add(new Student(1001,"张三",21));
treeSet.add(new Student(1002,"李四",18));
treeSet.add(new Student(1003,"王五",20));
treeSet.add(new Student(1004,"赵六",19));
System.out.println(treeSet);
3.3 定制排序
使用Comparable接口定义排序顺序有局限性:实现此接口的类只能按compareTo()定义的这一种方式排序。
如果需要更加灵活地排序,我们可以自定义(Comparator)比较器,在创建TreeSet集合对象时把我们自定义的比较器传入,则可以TreeSet会按照我们的比较器中定义的规则进行排序。
自定义比较器类,需要实现Comparator接口。Comparator接口只有一个抽象方法需要实现:public int compare(Object a, Object b);
判断规则:
返回 0,表示a == b
返回正数,表示b > b
返回负数,表示a < b
创建TreeSet集合对象时,把自定义比较器对象传入即可,TreeSet会自动按照比较器中的规则进行排序。
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
//按照年龄的降序排序
return stu2.getAge() - stu1.getAge() ;
}
});
treeSet.add(new Student("张三",21));
treeSet.add(new Student("李四",18));
treeSet.add(new Student("王五",20));
treeSet.add(new Student("李琦",19));
System.out.println(treeSet);
}
}