Set接口:不允许数据重复
Set接口没有扩充方法
Set接口常用子类
HashSet(无序存储)
- 底层使用哈希表+红黑树
- 允许存放null
TreeSet(有序存储):
- 底层使用红黑树
- 不允许出现空值
- 自定义类要想保存到TreeSet中,要么实现Comparable接口,要么向TreeSet传入比较器(Comparator)
Comparable接口与Comparator接口区别:
在JAVA 中,若想实现自定义类的比较,提供了以下两个接口
Java.lang.Comparable接口(内部比较器)-------排序接口:
若一个类实现了接口,就意味着该类支持排序
存放该类的conllection或数组,可以直接通过Collection.sort()或Arrays.sort进行排序
实现了Comparable接口的类可以直接存放在TreeSet或TreeMap中。
public int compareTo(T o);
返回值三种情况:
返回正数:表示当前对象大于目标对象
返回0:表示当前对象等于目标对象
返回负数:表示当前对象小于目标对象
Comparator(外部排序接口)---比较灵活
若要控制某个自定义类的顺序,而该类本身不支持排序(类本身没有实现Comparable接口)我们可以建立一个该类的“比较器”来进行排序。比较器实现Comparator接口即可
“比较器”:实现了Comparator接口的类作为比较器,通过该比较器来进行类的排序
int compare(T o1, T o2);
返回值与compareTo返回值完全一样
返回正数:O1>O2
返回0:O1=O2
返回负数:O1<O2
实现了Comparator接口进行第三方排序----策略模式,此方法更加灵活,可以轻松改变策略进行第三方的排序算法
boolean equals(Object obj);
代码如下:
import java.util.*;
class Person{
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//Person类的比较器
class AscAgeComparator implements Comparator<Person>{//年龄的升序
@Override
public int compare(Person o1, Person o2) {
return o1.getAge ()-o2.getAge ();
}
}
public class wl {
public static void main(String[] args) throws Exception {
Set<Person> set=new TreeSet<> (new AscAgeComparator ());
set.add (new Person("张",20));
set.add (new Person("李",18));
System.out.println (set);
}
}
结果:
[Person{name='李', age=18}, Person{name='张', age=20}]
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
Comparable接口与Comparator接口的关系:
Comparable是排序接口,若一个类实现了Comparable接口,意味着该类支持排序,是一个内部比较器(自己去和别人比)
Comparator接口是比较器接口,类本身不支持排序,专门有若干个第三方的比较器(实现了Comparator接口的类)来进行类的排序,是一个外部比较器(策略模式)
重复元素的比较:
TreeSet和TreeMap依靠Comparable与Comparator接口来区分重复元素
自定义类想要保存在TreeSet或TreeMap中:
- 要么该类直接实现Comparable接口,覆写compareTo方法
- 要么实现一个比较器传入TreeSet或TreeMap来进行外部比较(为0时,证明数据重复)
而HashSet与HashMap并不依赖比较接口。此时要想区分自定义元素是否重复,需要同时覆写equals与hashCode方法。
首先覆写equals()方法来判断两个元素内容是否相等
覆写equals方法原则;
- 自反性:对于任何非空引用值x,x.equals(x)都应返回true,(自己和自己比,一定是真的)
- 对称性:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y),才应返回true
- 传递性:对于任何非空引用值x和y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,则x.equals(z)一定返回true
- 一致性:对于任何非空引用值x和y,若x与y 的属性没有改变,则多次调用x.equals(y)始终返回true或始终返回false
- 非空性:对于任何非空引用值x,x.equals(null)一定返回false
先调用Hashcode计算出对象hash码决定存放的数据桶,而后只用equals方法来比较元素是否相等,若相等,则不在放置元素,若equals返回false,则在相同桶之后,使用链表将元素连接起来
Object 类提供的Hashcode方法使用对象的地址进行哈希
如果要想标识出对象的唯一性,一定需要equals()与hashCode()方法共同调用。
对象判断必须两个方法equals()、hashCode()返回值都相同才判断为相同。
哈希表:优化查找次数