一、set集合
1.1 set集合的特点
- 加入集合的顺序与取出集合的顺序不一定
- 没有索引
- 存储元素不能重复
1.2 哈希值
哈希值:是JDK根据u第项的地址或者数字运算出来的int类的数值;
Object类中有一个方式可以获取哈希值, public int hashCode();返回哈希值。
对象哈希值的特点 :
- 同一个对象多次调用和hashCode()方法返回的哈希值是相同的。
- 默认情况下,不同对象的哈希值是不同,但可以通过重写hashCode方法使得哈希值相同。
1.3. set集合应用场景
- 去重。因为Set集合中的元素不允许重复,所以可以用来去除列表或其他数据结构中的重复元素。
- 数学操作。由于Set集合支持并集、交集、差集等数学运算,所以它常用于对数据进行数学处理。
- 缓存。Set集合的查询速度非常快,这使得它成为一个很好的缓存工具。我们可以把需要频繁查询的数据存储在Set集合中,随时使用。
- 查找元素。Set集合提供了基于哈希表的查找功能,它可以快速地判断一个元素是否存在于集合中。
- 过滤器。Set集合可以作为过滤器使用,我们可以将一些不需要的元素存储在Set集合中,然后通过过滤器将这些元素从数据中过滤掉。
❕❕❕平常工作中,在调用第三方工具接口时可能会返回重复数据,这时可用到set集合存储进行去重
二、遍历方式
2.1foreach
2.2iterator (迭代器)
package com.ycxw.set; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * set集合的两种遍历方式 * 1.foreach * 2.iterator * * @author 云村小威 * */ public class Text_01 { public static void main(String[] args) { Set hs = new HashSet<>(); // 添加元素 hs.add(new String("小黑子")); hs.add("ikun"); hs.add(new String("小母鸡")); hs.add("绿湿涵"); // foreach遍历输出 for (Object object : hs) { System.out.println(object); } // iterator遍历 Iterator i = hs.iterator(); while (i.hasNext()) { System.out.println(i.next()); } } }
三、HashSet存储去重原理
3.1 HashSet集合的特点
- 底层数据结构是哈希表
- 对集合的比迭代是无序的,也就是说不保证存储和取出的先后顺序一致;
- 没有带索引的方法,所以不能用普通的for循环遍历, 但可以用加强for和 iterator 迭代器遍历
- 由于是Set集合,所以不会存储重复元素
3.2 HashSet唯一性原理
新添加到HashSet集合的元素都会行比较,首先会调用HashCode方法比较元素的哈希值,如果哈希值不同则将元素添加到集合(qeuals方法不会调用)
如果哈希值相同,则还会调用equals方法进行比较,如果返回flase(说明新添的元素与已有的元素的属性值不相同) 就添加到集合
如果调用equals方法进行比较如果,返回true(说明新添的元素与已有的元素的属性值相同) 则不会添加到集合
如图所示,添加了重复元素及对象,输出时是去重的且无序。
注意:如果存储的是实体对象则必须重写hashCode和equals方法,不然不会去重。
package com.ycxw.set; import java.util.HashSet; import java.util.Set; /** * * @author 云村小威 * */ public class Text_02 { public static void main(String[] args) { Set hs = new HashSet<>(); // 添加元素 hs.add(new Person("ikun", 18)); hs.add(new Person("珍爱粉", 20)); hs.add(new Person("ikun", 18)); // foreach遍历输出 for (Object object : hs) { System.out.println(object); } } } class Person { private String name; private int 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; } public Person(String name, int age) { super(); this.name = name; this.age = age; } // @Override // public int hashCode() { // final int prime = 31; // int result = 1; // result = prime * result + age; // result = prime * result + ((name == null) ? 0 : name.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; // Person other = (Person) obj; // if (age != other.age) // return false; // if (name == null) { // if (other.name != null) // return false; // } else if (!name.equals(other.name)) // return false; // return true; // } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
如上操作我把重写的hashCode和equals方法注释,所以能存储重复的对象。
四、TreeSet(自然排序,比较器排序)
4.1 TreeSet 集合特点
- 元素有序:这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方法取决于构造方法。
- 实现Compareator接口根据其元素的自然排序进行排序;
- TreeSet (Comparator comparator) 根据指定的比较器进行排序
- 没有索引的方法:所以不能用普通的for循环遍历,可以用加强for或迭代器iterator
- 由于是Set集合,所以不包含重复元素
4.2 自然排序Comparable的使用
package com.ycxw.set; import java.util.TreeSet; /** * TreeSet自然排序Comparable的使用 * @author 云村小威 * */ public class Text_02 { public static void main(String[] args) { TreeSet ts = new TreeSet<>(); // 添加元素 ts.add(new Person(1, "ikun", 18, 10000)); ts.add(new Person(2, "珍爱粉", 20, 100000)); ts.add(new Person(4, "陆诗涵", 21, 6000)); ts.add(new Person(3, "纯路人", 18, 3000)); // foreach遍历输出 for (Object object : ts) { System.out.println(object); } } } // 创建一个实体实现Comparable接口 class Person implements Comparable<Person> { private int id; // 编号 private String name; // 名称 private int age; // 年龄 private int yue; // 余额 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getYue() { return yue; } public void setYue(int yue) { this.yue = yue; } public Person(int id, String name, int age, int yue) { super(); this.id = id; this.name = name; this.age = age; this.yue = yue; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + yue; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (age != other.age) return false; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (yue != other.yue) return false; return true; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + ", yue=" + yue + "]"; } @Override public int compareTo(Person o) { // this.id 是被比较的对象及属性 (升序) // return o.id - this.id (降序) return this.id - o.id; } }
这时默认根据编号排序,那如何修改呢?
@Override
public int compareTo(Person o) {
// this.id 是被比较的对象及属性 (升序)
// return o.id - this.id (降序)
return this.id - o.id;
}
只需修改返回别比较的属性的即可。
这里注意如不实现Comparable接口则会报这个错误
Exception in thread "main" java.lang.ClassCastException: com.ycxw.set.Person cannot be cast to java.lang.Comparable
4.3 比较器排序Comparator 的使用
package com.ycxw.set; import java.util.Comparator; import java.util.TreeSet; /** * TreeSet比较器排序Comparable的使用 * * @author 云村小威 * */ public class Text_03 { public static void main(String[] args) { TreeSet ts = new TreeSet<>(new Comparator<User>() { @Override public int compare(User o1, User o2) { // 根据年龄进行降序排序 return o2.getAge() - o1.getAge(); } }); // 添加元素 ts.add(new User(1, "ikun", 18, 10000)); ts.add(new User(2, "珍爱粉", 20, 100000)); ts.add(new User(4, "陆诗涵", 21, 6000)); ts.add(new User(3, "纯路人", 19, 3000)); // foreach遍历输出 for (Object object : ts) { System.out.println(object); } } } // 创建一个实体实现Comparable接口 class User implements Comparable<User> { private int id; // 编号 private String name; // 名称 private int age; // 年龄 private int yue; // 余额 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getYue() { return yue; } public void setYue(int yue) { this.yue = yue; } public User(int id, String name, int age, int yue) { super(); this.id = id; this.name = name; this.age = age; this.yue = yue; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + yue; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (age != other.age) return false; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (yue != other.yue) return false; return true; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + ", yue=" + yue + "]"; } @Override public int compareTo(User o) { // TODO Auto-generated method stub return 0; } }
如我的需求事先比较年龄再比较余额呢?
@Override
public int compare(User o1, User o2) {
int num = o2.getAge() - o1.getAge(); //上一个对象 - 当前对象 降序
//判断num如果等于0就代表年龄是一样的
if(num==0) {
//则按余额进行降序
return o2.getYue() - o1.getYue();
}
// 根据年龄进行降序排序
return num;
}
});
根据自身需求修改此处即可;
总之,Set集合是一种非常常用的数据结构,它可以快速地进行查找、插入和删除操作,并且支持数学中的集合运算,被广泛应用于各种计算机程序的实现中。