文章目录
一.HashSet类
-
Set是Collection子接口,模拟了数学上的集的概念
-
Set只包含了从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素.
-
当试图添加两个相同元素近Set集合,添加操作失败,add()方法返回false.
-
Set集合存储特点:
- 不允许元素重复
- 不会记录元素的先后添加顺序
-
Set判断两个对象是否相等使用equsls,而不是使用 == ,也就是说,两个对象equals比较返回true.
Set集合是不会接受这两个对象的.
新建HashSetDemo
public class HashSetDemo {
public static void main(String[] args) {
//无序的,不重复的
Set s = new HashSet();
s.add("a");
s.add(3);
s.add(3);
s.add(0);
s.add("Y");
System.out.println(s.toString());
//addAll
Set<String> ss = new HashSet<>();
ss.add("S");
ss.add("A");
ss.add("B");
ss.add("C");
System.out.println(ss.toString());
Set<String> sss = new HashSet<>();
sss.add("a");
sss.add("b");
sss.add("c");
ss.addAll(sss);
System.out.println(ss);
//迭代器
Iterator it = ss.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//remove
System.out.println(ss.remove("c"));
System.out.println(ss);
//remove
System.out.println(ss.removeAll(sss));
System.out.println(ss);
}
}
二.equals和hashCode方法
-
HashSet是Set接口最常用的实现类,顾名思义,底层才用哈希表(散列/hash)算法.
其底层其实也是一个数组,存在的意义是提供查询速度,插入速度也比较快,但是适用于少量数据的插入
操作. -
在HashSet中如何判断两个对象是否相同问题:
- 两个对象的equals比较相等,返回true,则说明是相同对象
- 两个对象的hashCode方法返回值相等
-
二者:缺一不可.
当往HashSet集合中添加新的对象的时候,先会判断该对象和集合对象中hashCode值扫描二维码关注公众号,回复: 10437738 查看本文章- 不等:直接把该新的对象存储到hashCode指定的位置
- 相等:再继续判断新对象和集合对象中的equals做比较
- hashCode相同,equals为true,则视为是同一个对象,则不保存在哈希表中
- hashCode相同,equals为false:非常麻烦,存储在之前对象同槽位的链表上(拒绝)
-
不同数据类型的hashCode值
Eclipse可以自动生成hashCode和equals方法
- 对象的hashCode和equals方法的重要性:
- 每一个存储到hash表中的对象,都得提供hashCode和equals方法,用来判断是否同一个对象.
- 存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法.并且保证equals相等的时候,hashCode
也应该相等. - 如果需要把我们自定义的对象存储到哈希表中,该类型,的对象应该覆盖equal和hashCode方法,
并在该方法中提供自己的判断规则. - 可以使用Eclipse工具自动生成hashCode和equals方法.
新建类HashSetDemo2
//证明只有覆盖了equals和hashCode对象才能相等
class A{
public boolean equals(Object obj) {
return true;
}
}
class B{
public int hashCode() {
return 1;
}
}
class C{
public boolean equals(Object obj) {
return true;
}
public int hashCode() {
return 2;
}
}
public class HashSetDemo2 {
public static void main(String[] args) {
Set set = new HashSet();
set.add(new A());
set.add(new A());
set.add(new B());
set.add(new B());
set.add(new C());
set.add(new C());
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
新建类HashSetDemo3
//hashCode equals
class Student {
//定义name , age ,score
private String name;
private Integer age;
private Double score;
//构造方法
public Student(String name, Integer age, Double score) {
super();
this.name = name;
this.age = age;
this.score = score;
}
//hashCode 和equals
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((score == null) ? 0 : score.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (score == null) {
if (other.score != null)
return false;
} else if (!score.equals(other.score))
return false;
return true;
}
}
public class HashSetDemo3 {
public static void main(String[] args) {
//测试
Set set = new HashSet();
set.add(new Student("张三", 15, 78.8));
set.add(new Student("李四", 15, 78.8));
set.add(new Student("张思", 15, 89.8));
set.add(new Student("张三", 15, 78.8));
set.add(new Student("张三", 34, 78.8));
//name age score 相同的才为同一个对象
System.out.println(set.size());
//使用foreach遍历
for (Object ele : set) {
System.out.println(ele);
}
}
}
三.LinkedHashSet类
- List接口:允许元素重复,记录先后添加顺序
- Set接口:不允许元素重复,不记录先后添加顺序
- 需求,不允许元素重复,但是需要保证先后添加顺序
- LinkedHashSet:底层才有哈希表和链表算法.
- 哈希表:来保证唯一性,此时就是HashSet,在哈希表中元素没有先后顺序.
- 链表:来记录元素的先后添加顺序.
新建类LinkedHashSetDemo
//LinkedHashSet
public class LinkedHashSetDemo {
public static void main(String[] args) {
//有序
Set set = new LinkedHashSet();
set.add("sdsa");
set.add("sd");
set.add(89);
set.add(1);
System.out.println(set);
}
}
四.TreeSet类
注意:必须保证TreeSet集合中的元素对象是相同的数据类型,否则报错.
新建类TreeSetDemo
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<String> tset = new TreeSet<>();
tset.add("B");
tset.add("A");
tset.add("10");
tset.add("08");
System.out.println(tset);
System.out.println(tset.first());
System.out.println(tset.last());
System.out.println(tset.headSet("10"));
}
}
五.自然排序-Comparable
- 自然排序(从小到大):
TreeSet调用集合元素的comparableTo方法来比较元素的大小关系,然后讲集合元素按照升序排列
java.util.Comparable接口,可比较的
覆盖public int compareTo方法,在该方法,里写比较规则. - 在该方法中,比较当前对象(this)和参数对象o做比较(严格来说是对象中的数据,比如按照对象的年龄)
this > o;返回正整数;
this < o;返回负整数
this == o;返回0
- 在TreeSet的自然排序中,认为如果两个对象,做比较的compareTo方法返回的是0,则认为是同一个对象.
新建类TrreSetDemo
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Person other) {
if (this.age > other.age) {
return 1;
} else if (this.age < other.age) {
return -1;
} else
return 0;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("庄塞", 14));
ts.add(new Person("接口塞", 74));
ts.add(new Person("庄送到", 34));
ts.add(new Person("里塞", 24));
Iterator it = ts.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
六.定制排序
(从大到小,按照名字的长短来排序等)
- 在TreeSet构造器中传递java.lang.Comparator对象,并覆盖public int compare(Object o1,Object o2)
在编写比较规则
![image][sort]
- TreeSet集合来说,要么使用自然排序,要么使用定制排序
自然排序(comparable):CompareTo方法返回0
定制排序(comparator):Compare方法返回0
修改类TreeSetDemo
//自然排序
class Person implements Comparable<Person> {
...
}
//名字长度比较器
class NameLenthComparator implements Comparator<Person> {
//编写比较规则
@Override
public int compare(Person o1, Person o2) {
if (o1.name.length() > o2.name.length()) {
return 1;
} else if (o1.name.length() < o2.name.length()) {
return -1;
}else {
//加入名字的长度相同,在按年龄大小排序
if (o1.age > o2.age) {
return 1;
} else if (o1.age < o2.age) {
return -1;
} else
return 0;
}
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
....
System.out.println("---------implements Comparator-------------");
TreeSet<Person> ts1 = new TreeSet<>(new NameLenthComparator());
ts1.add(new Person("庄塞", 14));
ts1.add(new Person("接口塞", 74));
ts1.add(new Person("庄送到", 34));
ts1.add(new Person("西门吹牛", 24));
//使用迭代器迭代
Iterator it1 = ts1.iterator();
while (it1.hasNext()) {
System.out.println(it1.next());
}
}
}
七.Set实现类的特点和性能
- 共同特点
- 都不允许元素重复
- 都不是线程安全的类
解决方案:Set s = Collections.synchronizedSet(Set对象);
-
HashSet:底层采用哈希表算法,查询效率极高.
判断两个对象是否相等的规则:- equals比较为true
- hashCode值相等
-
要求存在哈希表中的对象元素都得覆盖equals和hashCode方法
-
LinkedHashSet:
HashSet的子类,底层也采用的是哈希表算法,但是也使用了链表算法来维持元素的先后添加顺序,
判断两个对象相同的规则和HashSet相同
因为需要多使用一个链表来记录元素的顺序,所以性能相对HashSet较低
一般少用,如果要求一个集合,既要保证元素不重复,也需要记录添加先后顺序,才选择使用
linkedHashSet -
TreeSet:不保证元素的先后添加顺序,但是会对集合中的元素做排序操作,
底层才有红黑树算法(树结构,比较擅长做范围查询)
TrreSet要么采用自然排序,要么定制排序 -
自然排序:要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法
-
定制排序:要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口)
在比较器中覆盖compare方法,并编写比较规则 -
TreeSet判断元素对象重复规则:
compare/compareTo方法是否返回0,如果返回0,则视为是同一个对象. -
HashSet做等值查询效率高,TreeSet做范围查询效率高.
而我们更多的情况,都是做等值查询,在数据库的索引中做范围查询较多,所以数据结构主要用于做,
索引,用来提高查询效率