红黑树
(1)概述:自平衡的二叉查找树,不是高度平衡的(跟高度差关系不大),他的平衡通过自己的红黑规则决定。
(2)红黑规则
a) 每个节点都是红色或者黑色
b) 根节点必须是黑色
c) 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶子节点都是黑色
d) 如果某一个节点是红色,那么他的子节点必须是黑色(也就是相邻两个相连接的节点不能都为红色)
e) 对每一个节点,从该节点到其所有节点的简单路径上,均包含相同数目的黑色节点
(3)添加节点的默认颜色
添加节点时,默认为红色,这样效率高
(4)红黑树添加节点后如何保持红黑规则
a) 如果添加的节点为根节点,那么直接变成黑色
b) 如果添加的节点为非根节点
aa) 如果父节点是黑色,那么不需要进行任何操作,默认为红色就行
bb) 如果父节点是红色
aaa) 叔叔节点是红色
aaaa) 将父节点变为黑色,叔叔节点变为黑色
bbbb) 将祖父节点设为红色
cccc) 如果祖父节点是根节点,就将祖父节点设置为黑色
bbb) 叔叔节点是黑色
aaaa) 将父节点设为黑色
bbbb) 将祖父节点设置为红色
cccc)以祖父节点为支点进行旋转(具体左旋还是右旋根据实际情况)
HashSet
(1)特点
a) 底层数据结构是哈希表(JDK8里面:数组+链表+红黑树,JDK8之前:数组+链表)
b)无序: 不能保证存储和去除顺序完全一致
c) 无索引没有带索引的方法,所以不能使用普通for进行遍历
d) 无重复:里面的元素唯一,无重复
(2)哈希值(哈希码值):根据对象的地址值或者属性值算出来的int值
a) 类中如果没有重写hashCode,那么会使用Object类中的方法获取哈希值
public int hashCode():该方法使用的是对象的地址值进行计算的
b) 类中重写了hashCode,那么就使用对象的属性值进行计算
c) 特点:
aa) 同一个对象多次调用hashCode()获得的值是相同的
bb) 默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法可以实现让不同的对象有相同的哈希值
(3)哈希表作用:针对于哈希表进行去除重复
哈希表
(1)JDK1.8之前结构:数组+链表
(2)JDK1.8之后结构:节点数少于8个的时候采用数组+链表的结构,当节点数多于8个的时候,采用的是数组+红黑树的结构
Map
(1)特点:
a) 是双列集合,由键值对组成
b) 键不能重复,值可以重复
(2)应用场景:解决一一对应问题
键和值是一一对应的,每一个键只能找到自己对应的值
(3)常用方法
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
(4)Map获取功能
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
(5)遍历方式
a) 通过keySet进行遍历
aa) 获取所有键值(keySet()方法)
bb) 遍历所有键值,获取到每一个键值(使用增强for)
cc) 根据键值找到相应的值(使用 get(Object key)方法)
示例:
public class MapDemo01 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("111", "111");
map.put("222", "222");
map.put("333", "333");
//获取所有键的集合。用keySet()方法实现
Set<String> keySet = map.keySet();
//遍历键的集合,获取到每一个键。用增强for实现
for (String key : keySet) {
//根据键去找值。用get(Object key)方法实现
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
b) 通过entrySet进行遍历
aa) 获取所有键值对,得到的是一个Set集合( Set<Map.Entry<K,V>> entrySet())
bb) 遍历所有集合,得到每一个entry(使用增强for)
cc) 通过getKey()和getValue()方法进行访问数据
示例:
public class MapDemo02 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("111", "111");
map.put("222", "333");
map.put("333", "333");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
c) forEach进行遍历(lambda表达式)
public class MapDemo02 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("111", "111");
map.put("222", "333");
map.put("333", "333");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历
map.forEach((key,value)-> System.out.println(key + "," + value));
}
}
HashMap
(1) 特点
a) HashMap底层是哈希表结构的
b) 依赖hashCode方法和equals方法保证键的唯一
c) 如果键要存储的是自定义对象,需要重写hashCode和equals方法
(2)案例:
/*
* 案例需求
* 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
* 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
*/
// 学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
// 测试类
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
//创建学生对象
Student s1 = new Student("111", 30);
Student s2 = new Student("222", 35);
Student s3 = new Student("333", 33);
Student s4 = new Student("444", 33);
//把学生添加到集合
hm.put(s1, "西安");
hm.put(s2, "武汉");
hm.put(s3, "郑州");
hm.put(s4, "北京");
//遍历集合
Set<Student> keySet = hm.keySet();
for (Student key : keySet) {
String value = hm.get(key);
System.out.println(key.getName() + "," + key.getAge() + "," + value);
}
}
}
TreeMap
(1)特点:
a) TreeMap底层是红黑树结构
b)依赖自然排序或者比较器排序,对键进行排序
c) 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
(2)案例:
/*
* 案例需求
* 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
* 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
*/
// 学生类
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照年龄进行排序
int result = o.getAge() - this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
}
// 测试类
public class Test1 {
public static void main(String[] args) {
// 创建TreeMap集合对象
TreeMap<Student,String> tm = new TreeMap<>();
/* // 使用比较器
TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student student, Student t1) {
//按照年龄进行排序
int result = o.getAge() - this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
});*/
// 创建学生对象
Student s1 = new Student("xiaohei",23);
Student s2 = new Student("dapang",22);
Student s3 = new Student("xiaomei",22);
// 将学生对象添加到TreeMap集合中
tm.put(s1,"江苏");
tm.put(s2,"北京");
tm.put(s3,"天津");
// 遍历TreeMap集合,打印每个学生的信息
tm.forEach(
(Student key, String value)->{
System.out.println(key + "---" + value);
}
);
}
}