day09 红黑树、HashSet集合、Map集合、HashMap集合、TreeMap集合

目录

一、红黑树

1.1 红黑树概述

1.2 案例应用

二、HashSet集合

2.1 HashSet集合的概述和特点

2.2 HashSet集合的基本使用

2.3 哈希值

2.4 哈希表结构

2.4.1 哈希表JDK7

2.4.2 哈希表JDK8

2.5 Set接口小结

三、Map集合

3.1 Map集合概述和特点

3.2 Map集合的方法

3.2.1 Map集合的基本方法

3.2.2 Map集合的获取方法

3.3 Map的遍历方式

四、HashMap集合

4.1 HashMap集合的概述和特点

4.2 HashMap案例 

五、TreeMap集合

5.1 TreeMap集合的概述和特点

5.2 TreeMap案例


一、红黑树

1.1 红黑树概述

红黑树的特点
红黑树又称为平衡二叉B树
1.每一个节点可以是红或者黑
2.红黑树不是高度平衡的,它的平衡是通过"自己的红黑规则"进行实现的
红黑树的红黑规则有哪些
1. 每一个节点或是红色的,或者是黑色的
2. 根节点必须是黑色
3. 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
红黑树添加节点
    红黑树添加节点的默认颜色为红色,效率高

                         

红黑树添加节点的规则 

红黑树添加节点后如何保持红黑规则

 

1.2 案例应用

案例需求

- 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩,英语成绩),并遍历该集合
- 要求: 按照总分从高到低出现,如果语文成绩相同,看英语成绩,英语相同看数学,,......数学相同             最后看姓名,姓名相同则不存
public class Demo {
    public static void main(String[] args) {
        //创建对象,添加属性
        TreeSet<Student> set = new TreeSet<>();
        set.add(new Student("zhangsan", 80, 70, 60));
        set.add(new Student("zhangsan", 80, 70, 50));
        set.add(new Student("lisi", 80, 60, 60));
        set.add(new Student("wangwu", 60, 70, 60));

        //遍历打印
        Iterator<Student> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        //控制台打印如下
        //Student{name='wangwu', chinese=60, math=70, englishi=60,sum=190}
        //Student{name='lisi', chinese=80, math=60, englishi=60,sum=200}
        //Student{name='zhangsan', chinese=80, math=70, englishi=50,sum=200}
        //Student{name='zhangsan', chinese=80, math=70, englishi=60,sum=210}

    }
}

class Student implements Comparable<Student> {
    private String name;
    private int chinese;
    private int math;
    private int englishi;

    public Student() {
    }

    public Student(String name, int chinese, int math, int englishi) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.englishi = englishi;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglishi() {
        return englishi;
    }

    public void setEnglishi(int englishi) {
        this.englishi = englishi;
    }

    public int getSum() {
        return chinese + englishi + math;
    }


    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", chinese=").append(chinese);
        sb.append(", math=").append(math);
        sb.append(", englishi=").append(englishi);
        sb.append(",sum=").append(getSum());
        sb.append('}');
        return sb.toString();
    }

    //重写Comparable中的compareTo方法
    @Override
    public int compareTo(Student o) {
        // 主要条件: 按照总分进行排序
        int result = this.getSum() - o.getSum();

        // 次要条件: 如果总分一样,就按照语文成绩排序
        result = result == 0 ? this.getChinese() - o.getChinese() : result;

        // 如果语文成绩也一样,就按照数学成绩排序
        result = result == 0 ? this.getMath() - o.getMath() : result;

        //如果数学成绩也一样,就按照英语成绩排序
        result = result == 0 ? this.getEnglishi() - o.getEnglishi() : result;

        // 如果总分一样,各科成绩也都一样,就按照姓名排序
        return result == 0 ? this.name.compareTo(o.name) : result;
    }
}

二、HashSet集合

2.1 HashSet集合的概述和特点

1.底层数据结构是哈希表
2.存取无序
3.不可以存储重复元素
4.没有索引,不能使用普通for循环遍历 

2.2 HashSet集合的基本使用

public class Demo {
    public static void main(String[] args) {
        HashSet<String>set = new HashSet<>();
        set.add("asd");
        set.add("asd");
        set.add("asd");
        set.add("sdfad");
        set.add("sdd");
        System.out.println(set);//[sdd, asd, sdfad]

        //循环遍历打印集合
        for (String s : set) {
            System.out.println(s);
            //控制台打印如下
            //sdd
            //asd
            //sdfad
        }
    }
}

2.3 哈希值

1.哈希值简介
  ​是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
2.如何获取哈希值
  ​Object类中的public int hashCode():返回对象的哈希码值
3.哈希值的特点
  - 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  - 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
public class Demo01 {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan",23);
        Student s2 = new Student("lisi",23);
        Student s3 = new Student("zhangsan",23);

        System.out.println(s1);

        //如果不重写hashCode方法,则会根据地址值打印哈希值
        //如果重写hashCode方法,则会根据属性值打印哈希值
        //同一个对象多次调用hashCode()方法返回的哈希值是相同的,因为地址值和属性值都相同
        System.out.println(s1.hashCode());//根据地址值985922955,根据属性值-1461068253
        System.out.println(s1.hashCode());//根据地址值985922955,根据属性值-1461068253
        System.out.println(s2.hashCode());//根据地址值1435804085,根据属性值102982116
        System.out.println(s3.hashCode());//根据地址值1784662007,根据属性值-1461068253
    }
}
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 student1 = (Student) o;

        if (age != student1.age) return false;
        return name != null ? name.equals(student1.name) : student1.name == null;

    }

    //重写hashCode方法
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}

2.4 哈希表结构

JDK8之前, 不包含JDK8, 哈希表底层采用"数组+链表"实现
JDK8之后, 哈希表底层进行优化, 采用"数组+链表+红黑"树实现

2.4.1 哈希表JDK7

JDK7底层原理解析 -> 数组+链表
   1. 创建默认长度为16, 默认加载因子为0.75的数组 ,数组名为table (数组存了16*0.75=12个元素时, 数组扩容为原先的两倍)
   2. 根据要存入元素的哈希值, 和数组的长度计算出应存入的位置
   3. 判断当前位置是否为null, 如果是null直接存入
   4. 如果不是null, 表示有元素, 会调用euqals方法比较属性
   5. 如果属性也一样则不存, 如果不一样则存入数组, 老元素挂在新元素下形成"链表结构"

2.4.2 哈希表JDK8

JDK8底层优化 -> 数组+链表+红黑树
问题:
   在JDK7时, 如果同一个位置存入多个元素, 那么会形成链表结构
   下一个要存入该位置的元素, 需要一个一个比较, 如果该链表过长, 效率非常低!
改进:
   在JDK8时, 如果某条链表长度达到8时, 就会自动将链表转为红黑树 (目的是为了提高效率)
案例
//在HashSet集合中添加Student对象
public class Demo {
    public static void main(String[] args) {
        //创建HashSet对象,将属性值添加进集合对象
        HashSet<Student> set = new HashSet<>();
        set.add(new Student("zhangsan", 23));
        set.add(new Student("lisi", 23));
        set.add(new Student("zhangsan", 23));
        System.out.println(set);
        //控制台打印
        /*
        [Student{name='lisi', age=23}, Student{name='zhangsan', age=23}]
         */
    }
}

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 String toString() {
        final StringBuilder sb = new StringBuilder("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }

    //重写hashCode和equals方法
    @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;
    }
}

2.5 Set接口小结

Set接口: 无序,无索引,不能重复
   HashSet实现类: 底层哈希表,如果存储自定义类,该类要重写hashCode和equals方法
   TreeSet实现类: 底层红黑树,可以排序,默认使用自然排序,也可以指定规则使用比较器排序

三、Map集合

3.1 Map集合概述和特点

Map集合概述
    interface Map<K,V>; K代表键的数据类型, V代表值的数据类型

Map集合的特点
    1.双列集合,一个键对应一个值
    2.键不可以重复,值可以重复
    3.键+值这个整体称为"键值对"或者"键值对对象", 在Java中也称"Entry对象"
    4.创建Map集合的对象, 通过多态的方式, 使用其实现类对象HashMap

3.2 Map集合的方法

3.2.1 Map集合的基本方法

方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
public class Demo {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        //V put(K key,V value) 添加元素
        map.put("123", "456");
        map.put("111", "555");
        map.put("123", "369");
        map.put("333", "666");
        map.put("222", "666");
        System.out.println(map);//{111=555, 123=369, 222=666, 333=666}

        //V remove(Object key) 根据键删除键值对元素
        String value = map.remove("111");
        System.out.println(value);//555

        //void clear() 移除所有的键值对元素
        //map.clear();

        //boolean containsKey(Object key) 判断集合是否包含指定的键
        boolean b = map.containsKey("222");
        System.out.println(b);//true

        //boolean containsValue(Object value) 判断集合是否包含指定的值
        boolean b1 = map.containsValue("456");
        System.out.println(b1);//true

        //boolean isEmpty() 判断集合是否为空
        boolean empty = map.isEmpty();
        System.out.println(empty);//false

        //int size() 集合的长度,也就是集合中键值对的个数
        int size = map.size();
        System.out.println(size);//3
        System.out.println(map);
    }
}

3.2.2 Map集合的获取方法

方法名说明
V get(Object key)根据键获取值
Set<K> keySet()获取所有键的集合
Collection<V> values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合
public class Demo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("123", "456");
        map.put("222", "999");
        map.put("333", "888");
        map.put("444", "777");
        // V get(Object key) 根据键获取值
        System.out.println(map.get("444"));//777

        // Set keySet() 获取所有键的集合
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key);
            /*
            控制台打印
            123
            222
            333
            444*/
        }

        // Collection values() 获取所有值的集合
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
            /*
            控制台打印
            456
            999
            888
            777*/
        }

        // Set> entrySet() 获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey()+"--"+entry.getValue());

            /*
            控制台打印
            123--456
            222--999
            333--888
            444--777*/
        }
    }
}

3.3 Map的遍历方式

public class Demo {
    public static void main(String[] args) {

        //创建集合对象
        Map<String,String>map = new HashMap<>();

        //V put(K key, V value) 将指定的值与该映射中的指定键相关联
        map.put("001","张三");
        map.put("002","李四");
        map.put("003","王五");
        map.put("004","张三");
        map.put("005","张三");
        map.put("006","张三");
        map.put("006","hhh");

        //第一种遍历方式, 获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        //遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> entry : entries) {
            //根据键值对对象获取键和值
            System.out.println(entry.getKey()+"----"+entry.getValue());
        }
        //控制台打印
       /* 001----张三
        002----李四
        003----王五
        004----张三
        005----张三
        006----hhh*/

        //第二种遍历方式,获取所有键的集合,用keySet()方法实现
        Set<String>keys = map.keySet();
        //遍历键的集合,获取到每一个键,用增强for实现
        for (String key : keys) {
            //根据键去找值,用get(Object key)方法实现
            String value = map.get(key);
            System.out.println(key+"----"+value);

        }

        //第三种遍历方式
        map.forEach(
                (k,v)->{
                    System.out.println(k+"===="+v);
        }
        );

        //第四种遍历方式
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String s, String s2) {
                System.out.println(s+"==="+s2);
            }
        });

    }
}

四、HashMap集合

4.1 HashMap集合的概述和特点

   1. HashMap是是Map接口的实现类
   2. HashMap跟HashSet一样, 底层是哈希表结构的
   3. 依赖HashCode方法和equals方法保证"键"的唯一
   4. 如果"键"要存储的是自定义类的对象, 那么要重写该类的HashCode和equals方法

4.2 HashMap案例 

案例需求

创建一个HashMap集合,键是学生对象(Student),值是居住地 (String),存储多个元素,并遍历。
要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象.
public class Demo {
    public static void main(String[] args) {
        Map<Student, String> map = new HashMap<>();
        map.put(new Student("zhangsan", 23), "美国");
        map.put(new Student("zhangsan", 23), "英国");
        map.put(new Student("wangmazi", 23), "中国");
        map.put(new Student("lisa", 24), "中国");

        //第一种遍历方式, 获取键的集合, 再根据键依次获取值
        Set<Student> keys = map.keySet();
        for (Student key : keys) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }

        //第二种遍历方式, 获取键值对集合, 再分别获取键和值
        Set<Map.Entry<Student, String>> entries = map.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            System.out.println(entry.getKey() + "---" + entry.getValue());
        }

        //第三种遍历方式, foreach接收实现类对象, 可以使用lambda优化
        map.forEach(
                (k, v) -> {
                    System.out.println(k + "-----" + v);
                }
        );

        //第四种遍历方式foreach
        //在foreach底层相当于遍历了map集合, 然后将获取的键和值交给抽象方法accept
        //所以需要一个接口BiConsumer的实现类, 重写accept方法, 完成打印键和值即可
        map.forEach(new BiConsumer<Student, String>() {
            @Override
            public void accept(Student student, String s) {
                System.out.println(student + "-----" + s);
            }
        });
    }
}

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;
    }

    //键是自定义学生对象(Student), 所以要重写HashCode和equals方法
    @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;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }

    @Override
    public int compareTo(Student o) {
        int result = this.getAge()-o.getAge();
        return result==0? this.name.compareTo(o.name):result;
    }
}

五、TreeMap集合

5.1 TreeMap集合的概述和特点

1. TreeMap是Map接口的实现类
2. TreeMap跟TreeSet一样, 底层是红黑树结构的 (再看一遍分析即可)
3. 依赖自然排序, 或者比较器排序, 对"键"进行排序
4. 如果"键"要存储的是自定义类的对象, 那么要实现Comparable接口或者在创建TreeMap对象时给出比较器排序规则

5.2 TreeMap案例

案例需求
    创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历.
    要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
public class Demo {
    public static void main(String[] args) {

        //创建TreeMap集合,键是学生对象(Student),值是籍贯(String)
        //使用比较器排序
        //重写Comparator的compare方法
        TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int result = o1.getAge() - o2.getAge();
                return result == 0 ? o1.getName().compareTo(o2.getName()) : result;
            }
        });

        //在map集合中添加对象的属性和他们的籍贯
        map.put(new Student("zhangsan", 23), "美国");
        map.put(new Student("lisa", 22), "英国");
        map.put(new Student("wangmazi", 23), "日本");
        map.put(new Student("wangmazi", 22), "中国");
        map.put(new Student("liyang", 25), "韩国");

        //遍历集合
        //第一种遍历方式
        map.forEach(
                (k, v) -> {
                    System.out.println(k + "-----" + v);
                }
        );
        //控制台打印
        /*Student{name='lisa', age=22}-----英国
        Student{name='wangmazi', age=22}-----中国
        Student{name='wangmazi', age=23}-----日本
        Student{name='zhangsan', age=23}-----美国
        Student{name='liyang', age=25}-----韩国*/

        //第二种遍历方式
        map.forEach(new BiConsumer<Student, String>() {
            @Override
            public void accept(Student student2, String s) {
                System.out.println(student2 + "==" + s);
            }
        });

        //第三种遍历方式
        Set<Map.Entry<Student, String>> entries = map.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            System.out.println(entry.getKey() + "===" + entry.getValue());
        }

        //第四种遍历方式
        Set<Student> kes = map.keySet();
        for (Student key : kes) {
            String value = map.get(key);
            System.out.println(key+"---"+value);

        }
    }
}

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 String toString() {
        final StringBuilder sb = new StringBuilder("Student{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值