Set接口

set接口存储

1
2
3
4
5
6
7
8
/**
存储的数据特点:无序的、不可重复的元素
具体的:
以HashSet为例说明:
1.无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的
2.不可重复性:保证添加新的元素照equals()判断时,判断的值为Hashcode的值,不能返回true,即:相同的元素只能添加一个
如果HashSet集合中存储的是自定义类的对象(JavaBean),需要重写equals(),HashCode方法,利用HashCode()算出Hash值后,按照不同的情况利用equals()方法判断其中的值是否相同,然后做出不同的判断,因为HashSet集合中是没有重复的数据的
*/

元素添加过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
以HashSet为例
我们向HashSet中添加元素a,首先调用元素a所在类的HashCode()方法,计算元素a的哈希值,
此哈希值接着某种算法计算出在HashSet底层数组中的存放位置(即:索引位置,判断数组此位置上是否有元素)
如果此位置没有其他元素,则元素a添加成功。-->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素,则比较元素a与元素b的hash值)
-如果hash值不相同,则元素a添加成功。-->情况2
-如果hash值相同,进而需要调用元素a所在类的equals()方法(判断元素中值是否相同,set集合是不允许有重复的元素)
-equals()返回true,元素添加失败。
-equals()返回false,则元素a添加成功。-->情况3

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储
jdk 7:元素a放在数组中,指向原来的元素
jdk 8:原来的元素在数组中,指向元素a
总结:七上八下

HashSet底层:数组+链表的结构。(前提:jdk7)
*/

常用方法

1
2
3
/**
Set接口中没有额外定义新的方法,使用的都是Coeeection中声明的方法
*/

常用实现类

  • Collection接口:单例集合,用来存储一个一个对象
    • Set接口:存储无序的、不可重复的数据 –>高中讲的”集合”
    • HashSet:作为Set接口的主要实现类;线程是不安全的;可以存储null值,存储无序的、不可重复的数据
    • LinkedHashSet:作为HashSet的子类:遍历其内部数据时,可以按照添加的顺序遍历
      在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
      对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    • TreeSet:可以按照添加对象的指定属性,进行排序。

存储对象所在类的要求

HashSet/LinkedHashSet:

要求:向Set(主要指:HashSet、LinkedHashSet)中添加数据,其所在的类一定要重写hashCode()和equals(),其equals()判断hashCode()方法得到的hash值是否相等

当两个对象中封装相同的内容,如果没有重写equals()和hashCode()方法就是相同的类型的数据,遍历的时候只会输出一个值,但是重写了这两个方法后,返回的hash值不同,然后通过equals方法判断是不同的值,遍历输出的时候就都输出了

要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码

重写两个方法的小技巧:对象中用作equals()方法比较的Field,都是用来计算hashCode值。即:相等的对象必须具有相等的散列码

TreeSet的使用

TreeSet的使用说明

1
2
3
4
5
6
/**
1.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals
2.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()
3.向TreeSet中添加的数据,要求是相同类的对象
4.两种排序方式:自然排序(实现Comparable接口 和 定制排序(Comparator))
*/

TreeSet常用的排序方式

1
2
3
4
5
/**
两种排序方式:自然排序(实现Comparable接口) 和 定制排序(ComParator)
1.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
2.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()
*/

方式一:自然排序

在JavaBean中调用CompareTo接口重写其中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.atguigu.java;

import java.util.Objects;

public class Person implements Comparable{//实现Comparable接口
private String name;
private int age;

public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}

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;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

@Override
public boolean equals(Object o) {//重写equals方法,可以判断对象中的值
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}

//按照姓名从小到大排列,从小到大的话把this前添加负号就可以了
@Override
public int compareTo(Object o) {
if (o instanceof Person){
Person person = (Person) o;
return this.name.compareTo(person.name);
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Test
public void test1(){
TreeSet set = new TreeSet();

//失败:不能添加不同类的对象
// set.add(123);
// set.add(456);
// set.add("AA");
// set.add(new User("Tom",12));

//举例一:
// set.add(34);
// set.add(-34);
// set.add(43);
// set.add(11);
// set.add(8);

//举例二:
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));


Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}

}

方式二:定制排序

在当前类中调用匿名内部类,实现Compartor接口中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
public void test9(){
TreeSet<Object> set = new TreeSet<>();
new Comparator<>() {
//按照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;
return Integer.compare(p1.getAge(), p2.getAge());
} else {
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
set.add(new Person("Ash",22));
set.add(new Person("Tom",12));
set.add(new Person("Jerry",32));
set.add(new Person("Jim",2));
set.add(new Person("Mike",65));
set.add(new Person("Jack",33));
set.add(new Person("Jack",56));


Iterator iterator = set.iterator();
while(iterator.hasNext()){//判断是否还有下一个元素
System.out.println(iterator.next());//1.指针下移 2.将下移以后集合位置上的元素返回
}
}