对应数据结构中的哈希表:无序、不重复的非线性结构
set原码:
不能包含重复元素的的集合,通常情况,set包含单独的值(map是键值对)
二、.Set接口实现子类 1. HashSet 此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺 序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。 是一个存放链表的数组 1). 常见方法:add()、contains()
@Test
void testSet01() {
// HashSet 是java中的一个标准的哈希表
Collection nums = new HashSet<>();
nums.add(123);
nums.add(456);
nums.add(789);
nums.add(1);
nums.add(13);
nums.add(258);
nums.add(10);
System.out.println(nums);
}
和自己添加的值顺序不同,无序
无序不是随机,会用特定的hash算法算值,而不是随机
@Test
void testSet01() {
// HashSet 是java中的一个标准的哈希表
Collection nums = new HashSet<>();
nums.add(123);
nums.add(456);
nums.add(789);
nums.add(1);
nums.add(13);
nums.add(258);
nums.add(10);
// add方法会返回一个boolean,表示是否添加成功
System.out.println(nums.add(31));
// 添加失败,返回false,表示之前已经存在了这个值
System.out.println(nums.add(31));
//是否包含某个元素
System.out.println(nums.contains(1));
System.out.println(nums.contains(11));
System.out.println(nums);
}
2). 哈希表遍历
注意:哈希表是无序的(没有下标),因此无法使用常规的循环,通过下标访问
(1)迭代器访问
@Test
void testSet02() {
// HashSet 是java中的一个标准的哈希表
Collection nums = new HashSet<>();
nums.add(123);
nums.add(456);
nums.add(789);
nums.add(1);
nums.add(13);
nums.add(258);
nums.add(10);
//迭代器访问
Iterator it = nums.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
(2)for循环加强进行遍历
for (Integer i : nums) {
System.out.println(i);
}
(3)forEach进行遍历
2. TreeSet:nums.forEach((i) ->System.out.println(i));
这个set是使用二叉树(红黑树)实现的一个集合,因此该集合是有序的
1). 排序规律(1). 字符串按accll码值排序
@Test
void testSet03() {
Set ss = new TreeSet<>();
// 有序的集合
ss.add("apply");
ss.add("huawei");
ss.add("mi");
ss.add("mm");
ss.add("vivo");
ss.add("OPPO");
System.out.println(ss);
}
(2). 数字按大小排序
@Test
void testSet04() {
Set ss = new TreeSet<>();
// 有序的集合
ss.add(1);
ss.add(2);
ss.add(33);
ss.add(333);
ss.add(12);
ss.add(102);
ss.add(50);
System.out.println(ss);
}
(3).对象
先定义一个Person类
package com.openlab.day20.entity;
public class Person {
private int id;
private String name;
private String nickname;
private String gender;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
public Person(int id, String name, String nickname, String gender, int age) {
super();
this.id = id;
this.name = name;
this.nickname = nickname;
this.gender = gender;
this.age = age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", nickname=" + nickname + ", gender=" + gender + ", age=" + age
+ "]";
}
}
@Test
void testSet06() {
Set ss = new TreeSet<>();
ss.add(new Person(1, "cc", "曹孟德", "男", 32));
ss.add(new Person(2, "lb", "刘玄德", "男", 28));
ss.add(new Person(3, "sq", "孙百万", "男", 18));
ss.add(new Person(4, "zf", "张翼德", "男", 24));
ss.add(new Person(5, "gy", "关云长", "男", 26));
ss.add(new Person(6, "hhd", "夏侯惇", "男", 43));
ss.add(new Person(7, "lb", "吕奉先", "男", 23));
ss.add(new Person(8, "dc", "貂蝉", "男", 16));
System.out.println(ss);
}
jvm不知道Person类应该怎样排序
String和Integer在定义的时候已经指定了排序规则,而我们自己写的Person没有制定规则,是让jvm自动去完成,但是jvm也不知道
注意:在java中,如果对象需要比较,则必须让该类实现Comparable接口
String和Integer(包装类)都实现了此接口
所以Person要想进行比较也需要实现Comparable接口
要求传递一个跟原对象进行比较
Comparable接口有个泛型,要求传递过来一个东西跟原对象进行比较,我们传递Person,比较比较同一个对象,不是同一个对象比较没有意义
重写方法:
该方法返回一个int的值
若返回一个 >0 的数,按照正序排序
若返回一个 <0 的数,按照降序排序
若返回一个 =0 的数,不变
@Override
public int compareTo(Person o) {
return 0;}
package com.openlab.day20.entity; public class Person implements Comparable{ private int id; private String name; private String nickname; private String gender; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person() { } public Person(int id, String name, String nickname, String gender, int age) { super(); this.id = id; this.name = name; this.nickname = nickname; this.gender = gender; this.age = age; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", nickname=" + nickname + ", gender=" + gender + ", age=" + age + "]"; } @Override public int compareTo(Person o) { // 指定需要排序的规则!!! // 指定规则:id的升序排列 // return this.getId() - o.getId(); // 指定规则:age的降序排列 return o.getAge() - this.getAge(); } }
return this.getId() - o.getId();
比较当前对象和下一个对象的id,如果大于零,相当于return 1,
代码相当于
int temp = this.getId() - o.getId();
if(temp > 0){
return 1;
}else{
return -1;
}
2). 去重
且TreeSet是有序的
HashSet就不是了
@Test
void testSet05() {
// 生成一个大小固定的list
List list = Arrays.asList(1,3,34,5,5,6,7,88,8,8,88,99);
Set ss = new TreeSet<>();
Set s = new HashSet<>();
ss.addAll(list);
System.out.println(ss);
s.addAll(list);
System.out.println(s);
}
Arrays.asList() 将不定长的数据以ArrayList的形式返回,返回的是一个固定大小的ArrayList(传了几个数据就是几个,不能再改变)
三、LinkedHashSet 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在 于,后者维护着一个运行于所有条目的双重链接列表。 此类提供所有可选的 Set 操作,并且 允许 null 元素。 与 HashSet 一样,它可以为基本操作( add 、 contains 和 remove )提供稳定的性能,假 定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet 稍逊一筹,不过,这一点例外: LinkedHashSet 迭代所需时间与 set 的 大小 成正比,而 与容量无关。 HashSet 迭代很可能支出较大,因为它所需迭代时间与其 容量 成正比。 此实现不是同步的。


