Set
Set是继承了Collection接口的抽象接口,不包含重复元素的集合。简单地说,集合不包含一对元素e1和e2,例如e1.equals(e2),最多包含一个空元素。
一、Set
/*
* Set元素特点:无序(添加顺序)不可重复
* TreeSet: 底层红黑树
* HashSet: 底层散列表
* */
public class _01_Collection_Set {
public static void main(String[] args) {
TreeSet<Object> treeSet = new TreeSet<>();
treeSet.add(1);
treeSet.add(2);
System.out.println(treeSet.size());
for (Object o : treeSet) {
System.out.println(o);
}
}
}
二、TreeSet
/*
* TreeSet:元素不可重复,添加的元素会按照某种规则自动排序
* 想要使用TreeSet,元素必须要排序
* 数字:默认从小到大
* 字符串:默认比较每位ASCII码
* 日期:默认比较自然日期 昨天、今天、明天
* */
public class _02_Collection_TreeSet {
public static void main(String[] args) {
TreeSet<Object> treeSet = new TreeSet<>();
treeSet.add(1);
treeSet.add(5);
treeSet.add(2);
treeSet.add(9);
// 上面添加的是数字,下面如果是字符串就会报错,因为添加的时候需要比较元素大小
// 而 不同类型 元素,没有可比性
// treeSet.add("aaa");
for (Object o : treeSet) {
System.out.println(o);
}
System.out.println("-------");
TreeSet<Object> treeSet2 = new TreeSet<>();
treeSet2.add("a");
treeSet2.add("1");
// 如果有多个字符,先比较第一位,如果第一位不同,则第一位小的在上面
// 如果第一位相同,在比较第二位,依次类推
treeSet2.add("11"); // 第一位为1,其ASCII码值为47,a的ASCII值97
treeSet2.add("101");
treeSet2.add("b");
treeSet2.add("c");
for (Object o : treeSet2) {
System.out.print(o + " "); // 输出:1 101 11 a b c
}
}
}
三、比较器Comparable和Comparator
/*
* Comparable
* 比较器有两种:1 元素自身比较器 2 比较器类
* 为什么字符串、Integer、Date可以排序?
* 因为都实现了 implement Comparable
* 使用TreeSet在进行数据添加的时候,会自动调用该对象的comparableTo()方法
* 和集合中元素进行比较
* 如果想要存储自定义类型?需要实现该接口
* */
public class _03_Collection_TreeSet {
public static void main(String[] args) {
TreeSet<Object> treeSet = new TreeSet<>();
User user = new User(10);
// 类型转换异常java.lang.ClassCastException:
// com.tianl.Collection.Set.User cannot be cast to java.lang.Comparable
// User必须实现Comparable接口才能存入TreeSet,添加的时候,会自动调用该对象的comparableTo()方法
// 没有实现comparableTo()方法,则不能添加入TreeSet
// treeSet.add(user);
treeSet.add(new User(12));
treeSet.add(new User(13));
treeSet.add(new User(15));
// treeSet.add(1); // 重复存入无效
System.out.println(treeSet.size());
for (Object o : treeSet) {
System.out.println(o);
}
}
}
class User implements Comparable{
private int age;
public User(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Object obj) {
// this表示当前对象
// obj 表示集合内对象
// 返回值为0 表示 相等 ,则不添加
// 返回大于0 表示 要添加的元素大,则放在后面
// 返回小于0 表示 要添加的元素小,则放在前面
User other = (User) obj;
return this.age - other.age; // 升序,
// return other.age - this.age; // 升序,
}
@Override
public String toString() {
return "User: [age=" + age + "]";
}
}
/*
// TreeSet中add方法源码,底层红黑树
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key); // 自动调用该对象的comparableTo()方法
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
* */
/*
* TreeSet添加元素必须排序
* 实现排序的两种方法
* 1 要添加的元素对应的类实现java.lang.Comparable接口,并实现comparableTo()方法
* 2 使用java.util.Comparator比较器类
* Comparable:添加元素实现该接口并覆写comparableTo()方法
* Comparator:第三方比较器类,应用于:Integer默认升序,如何实现降序?使用Comparator进行降序排序
*
* 如果添加元素的类 是 自定义类, 应该使用Comparable,可对扩展开放,其他人还可以使用Comparator实现新的排序
* 如果添加元素的类 不是 自定义类,
* 1 该类有排序(实现了Comparable),如Integer,但默认排序不满足需求
* 则可以使用Comparator调整排序,其优先级高
* 2 该类没有实现排序(没有实现Comparable),则需要使用Comparator进行排序,因为不可能修改源码实现
* */
public class _04_Collection_TreeSet {
public static void main(String[] args) {
// TreeSet<Object> treeSet = new TreeSet<>(); // 默认升序
// 实现Comparator接口compare()方法传入
//TreeSet<Object> treeSet = new TreeSet<>(new SortTest());
// 匿名内部类写法传入
TreeSet<Object> treeSet = new TreeSet<>(new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
}); // 实现降序
treeSet.add(2);
treeSet.add(4);
treeSet.add(8);
treeSet.add(16);
for (Object o : treeSet) {
System.out.println(o);
}
}
}
// 比较器类
class SortTest implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
}
/*
* TreeSet中使用Comparator排序demo实现多属性比较
* */
public class _01_Collection_Sort {
public static void main(String[] args) {
// Comparator
TreeSet<Object> treeSet = new TreeSet<>(new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
if (s1.getId() > s2.getId()) {
return 1;
} else if (s1.getId() < s2.getId()) {
return -1;
} else {
return s1.getAge() - s2.getAge(); // ID相同情况返回
}
}
});
treeSet.add(new Student(3,18,"AAA"));
treeSet.add(new Student(2,13,"AAB"));
treeSet.add(new Student(1,21,"AAC"));
treeSet.add(new Student(5,28,"AAG"));
treeSet.add(new Student(4,25,"AAD"));
treeSet.add(new Student(4,22,"AAE"));
treeSet.add(new Student(4,31,"AAF"));
for (Object o : treeSet) {
System.out.println(o);
}
}
}
// Student类中没有实现Comparable接口,则必须使用Comparator接口才能实现排序
class Student{
private int id;
private int age;
private String name;
public Student(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "id: " + id + ", age: " + age + ", name: " + name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/*
* List排序,想要实现排序,元素必须实现Comparable方法
* */
public class _02_Collection_Sort {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(21);
arrayList.add(16);
arrayList.add(11);
// 可以使用 Collections 包中的sort方法排序,是因为Integer中实现了Comparable接口
Collections.sort(arrayList);
System.out.println(arrayList);
}
}
/*
* 默认排序不满足需求,或者没有实现Comparable排序接口
* 则使用Comparator接口实现
* */
public class _03_Collection_Sort {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(1);
arrayList.add( 16);
arrayList.add(31);
// 使用Comparator接口实现降序
// 不改变源码条件下实现重新排序
Collections.sort(arrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2 - i1;
}
});
System.out.println(arrayList);
}
}
/*
* ArrayList下 需求:在main方法中,不改变其他源码情况下,输出语句按照年龄排序
* */
public class _04_Collection_SortTest {
public static void main(String[] args) {
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(new Person("AAA",18));
arrayList.add(new Person("BBB",28));
arrayList.add(new Person("CCC",8));
arrayList.add(new Person("DDD",8)); // 可重复
// Comparator接口实现按照年龄排序
Collections.sort(arrayList,new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;
// return Integer.compare(p1.getAge(), p2.getAge())
// 下面是上面语句的详细形式
if (p1.getAge() > p2.getAge()){
return 1; // 返回正数放在后边
} else if (p1.getAge() < p2.getAge()){
return -1; // 返回正数放在前边
} else {
return 0; // 相等
}
}
});
for (Object o : arrayList) {
System.out.println(o);
}
System.out.println("-----------");
for (Object o : arrayList) {
Person person = (Person) o;
System.out.println("name: "+person.getName()+", age: "+person.getAge());
}
}
}
class Person{
private int age;
private String name;
@Override
public String toString() {
return "name: " + name + ", age: " + age;
}
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
四、HashSet
/*
* hash算法:一种安全的加密算法,把不定长值变为定长值,不能保证唯一性
*
* 散列表:
* 数组中 保存链表() 单向链表,并且链表节点内有四个属性
* 1、key 2、value 3、next 4、hash
* 散列表是一种数据结构,在Java中被封装到HashSet、HashMap、HashTable中
* HashTable过时
* hash算法在Java中是指 hashCode 函数及重写
* 目的:为了查询快,因为hash值是一个固定的值
*
* 1 hash过程
* 获取对象,调用对象自身的hashCode()方法,进行hash算法,得到数组下标,把hash值保存到对应数组中
* Set特性:无序(添加顺序),不可重复(先hashCode比较,若hash值相等,在equals比较)
* 2 HashSet和HashMap
* HashSet是HashMap的封装,本质是一个HashMap
* 默认初始化容量都是 16
* 封装之后HashSet把value值屏蔽了,只能操作key。
* 所以在使用HashSet添加的时候,只需要传入key即可
* 3 HashSet添加过程
* 1 使用添加的键值对中的key,调用key的hashCode方法,生成hash值,进行hash算法得到数组下标
* 判断该下标上是否有元素,如果没有,把 键值对 保存到该数组中即可
* 2 如果该数组中有对象,则调用key的equals方法和数组中元素进行比较
* 如果相等则key不添加,value值覆盖
* 3 如果不等,则把 该对象 添加到已有元素的next属性,形成链表
* 4 如果添加时,已经是链表,则需要使用key和链表中所有元素的key进行比较是否相等
*
* 散列表中需要使用hashCode和equals来表示对象的唯一性
* 在添加自定义类型元素的时候,需要考虑按需求重写hashCode和equals方法
* */
public class _05_Collection_hashSet {
public static void main(String[] args) {
HashSet<Object> hashSet = new HashSet<>();
hashSet.add(111);
hashSet.add(222);
hashSet.add("xxx");
// hashSet.add("xxx"); // 相同不添加
for (Object o : hashSet) {
System.out.println(o);
}
}
}
/*
* 按需求重写hashCode和equals方法实现hashSet排序
* */
public class _06_Collection_hashSet {
public static void main(String[] args) {
HashSet<Object> hashSet = new HashSet<>();
hashSet.add(new Employee("30","不可计数V字领"));
hashSet.add(new Employee("45","护服务"));
hashSet.add(new Employee("83","v哈哈ds,,"));
hashSet.add(new Employee("18","AAA"));
hashSet.add(new Employee("19","BBB"));
hashSet.add(new Employee("20","CCC0"));
hashSet.add(new Employee("20","CCC1")); // 不添加,比较的是number
hashSet.add(new Employee("20","CCC2")); // 需求中:number相同则是同一个对象
for (Object o : hashSet) {
System.out.println(o);
}
}
}
class Employee{
private String number;
private String name;
public Employee(String number, String name) {
this.number = number;
this.name = name;
}
@Override
public int hashCode() {
System.out.println("hashCode()方法执行");
return number.hashCode(); // 只根据number属性判断自定义类类型
}
@Override
public boolean equals(Object obj) {
System.out.println("equals方法执行");
if (this == obj) return true;
if (obj == null) return false;
if (obj instanceof Employee){
Employee other = (Employee) obj;
return other.getNumber().equals(this.number);
}
return false;
}
@Override
public String toString() {
return "number: " + number + ", name: " + name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}