文章目录
1.Set系列集合特点
1.1 与List比较
List:有序,可重复,有索引
Set:无序,不重复,无索引
1.2 HashSet
添加的元素无序,不重复,无索引。内部数据结构是哈希表,是不同步的。
1.3 LinkedHashSet
添加的元素,有序,不重复,无索引。
1.4 TreeSet
按照大小默认升序排序,不重复,无索引。
关于排序:
-
字符串按照首字符排序。首字符相同时,看第二位,依此类推。
-
有值特性的元素(int,float等),按照数值大小排序。
-
自定义的引用类型,可以:
--------- 在对象类中重写Comparable接口的compareTo()方法。
--------- 为集合设置比较器对象,即重写Comparator接口的Compare()方法。
如果两样都被实现,优先使用比较器的比较规则。
ps: 具体代码实现,详见3.2.2
2.Set集合的两个问题
2.1 Set集合添加的元素如何去重合?
- 对于有值特性(String,Integer,int)的,直接判断进行去重复。
- 对于引用数据类型的类对象:
- hashCode
- equals
首先Set集合会让两两对象,先调用自己的hashCode()方法,判断两个对象的哈希值是否相同;如果相同,再用equals()方法判断内容是否相同。
如果哈希值不同,则无需判断内容是否相同。
2.2 Set集合无序的原因
Set集合添加元素无序是因为其底层用哈希表存储元素:
JDK1.8之前,哈希表 = 数组 + 链表 + (哈希算法)
JDK1.8之后,哈希表 = 数组 + 链表 + 红黑树 +(哈希算法)
哈希算法:
- 先获取元素对象的哈希值
- 用哈希值对底层数组的长度取余
- 取余的结果作为该元素在底层数组的索引位置
- 存入该元素对象
ps:如果索引位置相同,在该数组的位置形成链,依次连接。
用哈希表存储元素,增删改查性能好,但它无序,不重复。
同时考虑到链表可能过长影响性能,在JDK1.8之后,当链表长度超过8时,链表会 转成红黑树 。
ps:LinkedHashSet在底层依然是用哈希表存储元素,
但是每个元素额外带一个链来维护添加顺序,顺着这条链,有序查找输出元素。
3. 代码示例
3.1 用HashSet存储自定义对象
规定:如果学生类的姓名和年龄一样,则视为同一个对象。
分析:即使Student对象内的姓名和年龄完全相同,因为哈希值是根据对象地址得出的,所以把数值完全相同的对象往HashSet集合存储时,都会被存进去。但是现在规定姓名和年龄一样,则视为同一个对象,也就是存进去时前一个要被去掉。‘
所以:
通过重写hashCode()方法和equals()方法可实现规定内容。
做法:重写方法时,让哈希值根据name和age得出;如果哈希值相同,重写后的equals方法判断name和age的值是否相同,若相同,则该对象不会进入HashSet。
import java.util.HashSet;
import java.util.Iterator;
class Student {
private String name;
private int age;
Student() {
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode()+age;
}
@Override
public boolean equals(Object obj) {
Student s = (Student)obj;
return this.name.equals(s.name) && this.age==s.age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
public class HashCodeDemo {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Student("abc1",21));
hashSet.add(new Student("abc2",22));
hashSet.add(new Student("abc3",23));
hashSet.add(new Student("abc4",24));
hashSet.add(new Student("abc1",21));
Iterator iterator = hashSet.iterator();
while(iterator.hasNext()) {
Student s = (Student)iterator.next();
System.out.println( s.getName()+"————"+s.getAge() );
}
}
}
3.2 TreeSet代码示例
3.2.1 处理带值特性的对象(字符串等)
public class Demo {
public static void main(String[] args) {
TreeSet a = new TreeSet();
a.add("abc1");
a.add("abc2");
a.add("abc3");
a.add("abc3");
Iterator iterator = a.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
//abc1
//abc2
//abc3
}
}
}
3.2.2 处理无值特性的对象
必须要:实现Comparable接口或者实现Comparator接口
否则报错,因为不知道排序规则
3.2.2.1 实现Comparable接口
在Student类中实现Comparable接口,重写compareTo方法。
import java.util.Iterator;
import java.util.TreeSet;
class Student implements Comparable {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Object obj) {//根据年龄排序,如果年龄相同,按照名字排序
Student s = (Student)obj;
int temp = this.age-s.age;
return temp == 0 ? this.name.compareTo(s.name) : temp;
}
}
public class Demo {
public static void main(String[] args) {
TreeSet a = new TreeSet();
a.add(new Student("zxc1",21));
a.add(new Student("zxc2",22));
a.add(new Student("zxc5",22));
a.add(new Student("zxc3",23));
a.add(new Student("zxc3",23));
Iterator iterator = a.iterator();
while(iterator.hasNext()) {
Student s = (Student)iterator.next();
System.out.println(s.getName()+"——"+s.getAge());
}
}
}
3.2.2.2 实现Comparator接口
1.创造一个实现Comparator接口的类
让集合自身具备比较功能(Comparator比较器):重新定义一个类实现Comparator接口,重写compare方法;创建该类的对象作为参数放进TreeSet中。
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Student {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class Demo {
public static void main(String[] args) {
TreeSet a = new TreeSet(new ComparatorByAge());//在构造方法内,创建带比较器的对象作为参数
a.add(new Student("zxc1",21));
a.add(new Student("zxc2",22));
a.add(new Student("zxc5",22));
a.add(new Student("zxc3",23));
a.add(new Student("zxc3",23));
Iterator iterator = a.iterator();
while(iterator.hasNext()) {
Student s = (Student)iterator.next();
System.out.println(s.getName()+"——"+s.getAge());
}
}
}
class ComparatorByAge implements Comparator {
public int compare(Object o1,Object o2) {
Student s1 = (Student)o1;
Student s2 = (Student)o2;
int temp = s1.getAge()- s2.getAge();
return temp==0 ? s1.getName().compareTo(s2.getName()) : temp;
}
}
2.使用匿名内部类
用匿名内部类可以省去再写一个实现Comparator接口的类,更加方便
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Student {
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class Demo {
public static void main(String[] args) {
TreeSet<Student> a = new TreeSet<Student>(new Comparator<Student>() {//匿名内部类
@Override
public int compare(Student s1,Student s2) {
int temp = s1.getAge()-s2.getAge();
return temp==0 ? s1.getName().compareTo(s2.getName()) : temp;
}
});
a.add(new Student("zxc1",21));
a.add(new Student("zxc2",22));
a.add(new Student("zxc5",22));
a.add(new Student("zxc3",23));
a.add(new Student("zxc3",23));
Iterator iterator = a.iterator();
while(iterator.hasNext()) {
Student s = (Student)iterator.next();
System.out.println(s.getName()+"——"+s.getAge());
}
}
}