一 :Set和List的区别
注意:有序指元素存储顺序和取出顺序
List: 有序(存储顺序和取出顺序一致),可重复。
Set: 无序(存储顺序和取出顺序不一致),唯一。
问题:为什么存储字符串的时候,字符串相同的内容只能存储一个呢?代码如下:
package it.set;
import java.util.HashSet;
/**
* HaehSet字符串存储并遍历
* 问题:为什么存储字符串的时候,字符串相同的内容只能存储一个呢?
* 通过查看add的源码我们发现,这个方法的底层依赖HashCode()和equals()两个方法;
* 步骤:
* 首先比较哈希值
* 如果相同,继续走,比较地址值或者equals();
* 如果不同,就直接添加到集合中
* 按照方法的步骤来说:
* 先看HashCode()值是否相同,
* 相同 :继续走equals()方法,
* 返回true,说明元素重复,就不添加。
* 返回false,说明元素不重复,就添加到集合中。
* 不同:就直接把元素添加到集合中。
* 如果类没有重写这两个方法,默认使用的是Obiect()的,一般来说不会相同。
* 而String就重写了HashCode()和equals()方法,它就可以把内容相同的字符串去掉,只留下一个。
*/
public class HashSetTest {
public static void main(String[] args) {
//创建集合对象,
HashSet<String> set = new HashSet<String>();
//添加元素
set.add("hello");
set.add("world");
set.add("java");
set.add("world");
//遍历元素
for(String s:set){
System.out.println(s);
}
}
}
二 Set集合及他的子类HashSet和TreeSet
A:HashSet 特点:不保证Set的迭代顺序,特别是他不保证该顺序恒久不变。
问题:HashSet如何保证元素唯一性?
底层数据结构是哈希表(元素是链表的数组)
哈希表依赖于HashCode()和equals()两个方法;
练习:存储自定义对象,并保证唯一性/如果两个对象成员变量值都相同,则为同一元素。代码如下:
package Set.HashSet;
import java.util.HashSet;
/**
* 需求:存储自定义对象,并保证唯一性/如果两个对象成员变量值都相同,则为同一元素。
*
* 如果不重写学生类中的方法,则默认使用的是Object类,这个时候,
* 的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
*
*
* 由于HashSet集合的底层是哈希表结构,
* 而哈希表结构底层依赖HashCode()和equals()两个方法;
* 如果你认为对象的成员变量值相同,即为同一个对象的话,你就应该重写这两个方法,
* 如何重写呢?不用担心自动生成即可
*
*/
public class HashSetDome01 {
public static void main(String[] args) {
HashSet<StudentHashset> set = new HashSet<StudentHashset>();
//首先定义对象
StudentHashset s1 = new StudentHashset("唐僧",30);
StudentHashset s2 = new StudentHashset("孙悟空",29);
StudentHashset s3 = new StudentHashset("猪八戒",28);
StudentHashset s4 = new StudentHashset("唐僧",30);
StudentHashset s5 = new StudentHashset("沙和尚",27);
StudentHashset s6 = new StudentHashset("唐僧",27);
//添加元素
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
//遍历元素
for(StudentHashset s:set){
System.out.println(s.getName()+"======"+s.getAge());
}
}
}
对应的学生类,需要重写HashCode()和equals()方法
package Set.HashSet;
public class StudentHashset {
private String name;
private int age;
public StudentHashset(){
}
public StudentHashset(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 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;
StudentHashset other = (StudentHashset) 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;
}
}
B:TreeSet 特点:排序和唯一 ,能够让元素按照某种规则进行排序,1)自然排序 2)比较器排序(comparator接口)
第一 :自然排序(要想实现自然排序,必须实现Comparable<E>接口),代码如下:
package Set.TreeSet;
import java.util.TreeSet;
/****
* TreeSet 存储自定义对象并保证排序和唯一
* A: 你没有告诉我怎么排序?
* 自然排序,安照年龄从小到大排序
* B: 元素什么情况下算唯一,你也没告诉我?
* 成员变量值都相同即为同一个元素
*/
public class TreeSetDome02 {
public static void main(String[] args) {
//创建集合对象
TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>();
//创建元素
StudentTreeset s1 = new StudentTreeset("zhangxinxin",25);
StudentTreeset s2 = new StudentTreeset("lilibo",30);
StudentTreeset s3 = new StudentTreeset("zhangtao",19);
StudentTreeset s4 = new StudentTreeset("zhangxinxin",20);
StudentTreeset s5 = new StudentTreeset("lilibo",30);
StudentTreeset s6 = new StudentTreeset("guojiali",26);
StudentTreeset s7 = new StudentTreeset("aili",19);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
for(StudentTreeset s:ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
package Set.TreeSet;
/**
* 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口。
*
*/
public class StudentTreeset implements Comparable<StudentTreeset>{
private String name;
private int age;
public StudentTreeset(){
}
public StudentTreeset(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 int compareTo(StudentTreeset s) {
//这里返回什么,其实应该根据我的排序规则来做
//按照年龄排序,主要条件(添加那个对象,那个对象叫this,它要和其他元素进行比较)
int num = this.age - s.age;
// return num;
//次要条件 年龄相同的时候还要去看姓名是否相同
//如果年龄和姓名都相同,才是同一个元素
int num2 = num == 0 ? this.name.compareTo(s.name) : num; //如果年龄相同,继续比姓名,否则返回num年龄;
return num2;
}
}
第二:比较器排序(comparator接口)
package Set.TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
/****
* TreeSet 存储自定义对象并保证排序和唯一
* 比较器排序,如果一个方法的参数是接口,那么真正要的接口的实现类对象
*/
public class TreeSetDome03 {
public static void main(String[] args) {
//创建集合对象
//TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(); //自然排序
//public TreeSet(Comparator comparator); //比较器排序
//TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(new MyComparator());
TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(new Comparator<StudentTreeset>(){
@Override
public int compare(StudentTreeset s1, StudentTreeset s2) {
//姓名长度
int num = s1.getName().length()-s2.getName().length();
//姓名
int num1 = num==0?s1.getName().compareTo(s2.getName()):num;
//年龄
int num2 = num1==0?s1.getAge()-s2.getAge():num1;
return num2;
}
});
//创建元素
StudentTreeset s1 = new StudentTreeset("zhangxinxin",25);
StudentTreeset s2 = new StudentTreeset("lilibo",30);
StudentTreeset s3 = new StudentTreeset("zhangtao",19);
StudentTreeset s4 = new StudentTreeset("zhangxinxin",20);
StudentTreeset s5 = new StudentTreeset("lilibo",30);
StudentTreeset s6 = new StudentTreeset("guojiali",26);
StudentTreeset s7 = new StudentTreeset("aili",19);
//添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
for(StudentTreeset s:ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
最后总结:TreeSet集合保证元素排序和唯一性原理
唯一性:是根据比较的返回值是不是0来决定
排序:
A:自然排序(元素具备比较性)
让元素所属的类实现自然排序接口Comparable
B:比较器排序(集合具备比较性)
让集合的构造方法接收一个比较器接口的子类对象Comparator