集合框架(二)
Set集合
Set集合是一个无序不重复的接口,里面有两个实现类HashSet和TreeSet
HashSet类
创建HashSet的语法:
①HashSet 名称=new HashSet();
②HashSet set1=new HashSet(初始化容量数);
③HashSet set2=new HashSet(初始化容量数 ,负载因子);
负载因子:当数量到达容量的多少时进行扩容,默认75%。
举个例子:
1 public class Test2 { 2 public static void main(String[] args) { 3 //创建一个无参的HashSet对象, 4 HashSet set=new HashSet(); 5 //初始化容器的大小,初始化容量为12 6 HashSet set1=new HashSet(12); 7 //初始化容器的大小,并附上负载因子 8 HashSet set2=new HashSet(12,0.7f); 9 // (这个例子中)负载因子的概念是:当容量超过70%,进行扩容操作 10 } 11 }
添加操作
语法:
boolean add(E e):将指定元素添加到此集合(如果尚未存在)
举个例子:
1 public class Test2 { 2 public static void main(String[] args) { 3 //创建一个无参的HashSet对象, 4 HashSet set=new HashSet(); 5 //添加一个尚未存在的元素时返回true,否则返回false 6 set.add(12); 7 } 8 }
删除操作
语法:
boolean remove(Object o):删除指定的元素
举个例子:
1 public class Test2 { 2 public static void main(String[] args) { 3 //创建一个无参的HashSet对象, 4 HashSet set=new HashSet(); 5 //删除一个指定的元素 6 set.remove(12); 7 } 8 }
遍历操作
HashSet是一个无序不重复的结构,所以没有索引值,只能通过迭代器和增强for循环来实现遍历
①迭代器
1 public class Test2 { 2 public static void main(String[] args) { 3 HashSet set = new HashSet();//生成一个HashSet集合 4 Iterator iterator = set.iterator();//获取迭代器的对象 5 while (iterator.hasNext()) {//判断迭代器是否存在下一个元素 6 //存在下一个元素 7 //获取指定元素并将指针往下移动 8 Object object = iterator.next(); 9 System.out.println(object); 10 } 11 } 12 }
②通过foreach遍历
1 public class Test2 { 2 public static void main(String[] args) { 3 HashSet set=new HashSet();//生成一个HashSet集合 4 for(Object object:set){ 5 //遍历集合内容 6 System.out.println(object); 7 } 8 } 9 }
HashSet底层结构
Set是一个无序不重复的集合,而HashSet作为Set的子实现类,它是如何实现无序不重复的呢?
①底层使用哈希表实现无序
HashSet底层采用哈希表存储数据,哈希表是一种对于增删改查数据性能都比较好的结构,HashSet在存储数据的时候,会根据数组长度和哈希值计算出要存储的位置,从而实现无序结构。
②使用hashcode方法和equals方法实现不重复
HashSet通过hashcode方法计算哈希值,如果两者哈希值都相等,则使用equals方法来判断两者时候是同一个类型。
自定义类对象添加HashSet
1 public class Test2 { 2 public static void main(String[] args) { 3 HashSet<Person> set = new HashSet<>();//生成一个HashSet集合 4 /* 5 * TODO HashSet底层原理: 6 * 将自定义类添加到HashSet中时,会判断hashcode方法计算的哈希值是否相同 7 *如果哈希值不相等,则表示两者不是同一个对象,添加成功 8 * 如果哈希值相等,则比较equals方法,如果equals方法不相等,说明不是同一个对象 ----这种情况为哈希冲突 9 * 如果equals方法相等,则表示两者是同一个对象 10 */ 11 //添加对象 12 set.add(new Person("张三",18)); 13 set.add(new Person("张三",18)); 14 set.add(new Person("李四",14)); 15 set.add(new Person("王五",19)); 16 set.add(new Person("老牛",14)); 17 //遍历对象 18 for (Person person:set) { 19 System.out.println(person); 20 } 21 22 } 23 } 24 class Person{ 25 private String name; 26 private int age; 27 28 29 public Person() { 30 } 31 32 public Person(String name, int age) { 33 this.name = name; 34 this.age = age; 35 } 36 public String getName() { 37 return name; 38 } 39 public void setName(String name) { 40 this.name = name; 41 } 42 43 public int getAge() { 44 return age; 45 } 46 public void setAge(int age) { 47 this.age = age; 48 } 49 50 public String toString() { 51 return "Person{name = " + name + ", age = " + age + "}"; 52 } 53 }
效果展示:

这里发现,对于自定义类中,我想表达的是属性如姓名和年龄两者相等,则是同一个对象才对呀?那为什么会出现两个{张三,18}呢?
那是因为自定义类它使用的hashcode方法是Object类中的hashcode方法,而Object类中的hashcode方法是通过比较两者之间地址值,如果地址值相同表示同一个对象,所以如果我们想表示属性值相同表示同一个对象,需要重写hashcode方法。
重写hashcode语法
@Override public int hashCode() { return Objects.hash(需要比较的属性名,.......,需要比较的属性名);
}
效果展示:

发现为什么重写了hashCode仍然结果一样呢?
那是因为当我们只重写hashCode方法时,HashSet底层会认为{张三,18}这个对象求出的哈希值是相等的,它会比较equals方法,而如果我们没有重写equals方法会直接使用Object类的equals 方法,而Object类的equals方法比较的是两者地址值,如果地址值相等,才代表是同一个对象。
重写equals语法
@Override public boolean equals(Object obj) { //比较两者地址值,如果相等返回true if(obj == this){ return true; } //如果两者是不同类型的返回false if(!(obj instanceof 自定义类)){ return false; } //两者是相同类型 自定义类 对象 = (自定义类) obj; //如果两者属性相同,则表示同一个对象 if(比较对象的属性如果相等){ return true; } return false;
}
效果展示:

发现每个对象是不重复的,这样才能满足我们自己的需求。而String类它也是通过重写hashCode和equals方法来判断是否为同一对象.
TreeSet类
TreeSet底层使用的是二叉树,而TreeSet中元素要求具有排序规则,其它判断元素是否重复与HashSet一样都需要重写hashCode方法和equals方法
创建TreeSet语法:
TreeSet 名称 =new TreeSet(); //调用无参构造器
举个例子:
public class TreeSetTest { public static void main(String[] args) { //调用无参构造器 TreeSet set=new TreeSet(); } }
TreeSet存储的内容是可比较的,那如果是自定义类如何实现对象之间的可比较呢?
Java中提供两种方法来实现
比较的结果返回值:
如果返回0表示两个元素一致。
如果返回大于0,表示添加得元素比容器中得元素大。
如果返回小于0,表示添加得元素比容器中得元素小。
方法一:实现Comparable接口重写里面的方法
语法:
class 类名 implements Comparable<类名>{ // 重写CompareTo方法 @Override public int compareTo(类名 o) { //代码内容
}
举个例子:
1 public class TreeSetTest {
2 public static void main(String[] args) {
3 //调用无参构造器
4 TreeSet set=new TreeSet();
5 //添加元素
6 set.add(new People("张三",20));
7 set.add(new People("老王",18));
8 set.add(new People("老牛",30));
9 set.add(new People("李四",10));
10 //遍历元素
11 for (Object o:set) {
12 People people=(People) o;
13 System.out.println(people);
14 }
15 }
16 }
17 class People implements Comparable{
18 private String name;
19 private int age;
20
21 @Override
22 public int compareTo(People o) {
23 24 //按照年龄进行从小到大排序(根据对象o与本类对象进行比较)
25 return this.age-o.age;
26 }
27
28 public People() {
29 }
30
31 public People(String name, int age) {
32 this.name = name;
33 this.age = age;
34 }
35 public String getName() {
36 return name;
37 }
38 public void setName(String name) {
39 this.name = name;
40 }
41 public int getAge() {
42 return age;
43 }
44 public void setAge(int age) {
45 this.age = age;
46 }
47 public String toString() {
48 return "People{name = " + name + ", age = " + age + "}";
49 }
50 }
效果展示:

方法二:通过自定义排序方式,通过实现Comparator接口从而调用TreeSet的有参构造方法
语法:
class 类名 implements Compatator<类名>{ //重写接口方法 @Override public int compare(类名 o1,类名 o2){ //代码内容 } }
举个例子:
1 public class TreeSetTest {
2 public static void main(String[] args) {
3 //调用有参构造方法,传入一个实现Comparator接口的类对象
4 TreeSet<People> set=new TreeSet<>(new People());
5 //添加元素
6 set.add(new People("张三",20));
7 set.add(new People("老王",18));
8 set.add(new People("老牛",30));
9 set.add(new People("李四",10));
10 //遍历元素
11 for (People people:set){
12 System.out.println(people);
13 }
14 }
15 }
16 class People implements Comparator<People> {
17 private String name;
18 private int age;
19
20 @Override
21 public int compare(People o1, People o2) {
22 //按照年龄从小到大排序
23 return o1.age-o2.age;
24 }
25
26 public People() {
27 }
28
29 public People(String name, int age) {
30 this.name = name;
31 this.age = age;
32 }
33 public String getName() {
34 return name;
35 }
36 public void setName(String name) {
37 this.name = name;
38 }
39 public int getAge() {
40 return age;
41 }
42 public void setAge(int age) {
43 this.age = age;
44 }
45 public String toString() {
46 return "People{name = " + name + ", age = " + age + "}";
47 }
48 }
效果展示:

发现有时候这个实现Comparator接口的类只是使用一次就会丢弃,那如果每次都直接生成一个类只使用一次就会导致资源的浪费。
所以这里可以使用匿名实现类的方法来解决这个问题
语法:
Comparator<类名> comparator=new Comparator<类名>() { @Override public int compare(类名 o1, 类名 o2) { //代码内容 } };
举个例子:
1 public class TreeSetTest { 2 public static void main(String[] args) { 3 //生成匿名接口类对象 4 Comparator<String> comparator=new Comparator<String>() { 5 @Override 6 public int compare(String o1, String o2) { 7 return o1.length()-o2.length(); 8 } 9 }; 10 //调用有参构造器,传入自定义排序规则对象 11 TreeSet<String> set=new TreeSet<>(comparator); 12 } 13 }
这种方法也适用于一些已经实现Comparable接口的类,当自己不想用这种排序规则时,便可以使用自定义排序.

浙公网安备 33010602011771号