Java学习笔记day18-红黑树-Map

Day18

红黑树
  • 平衡二叉B树
  • 每一个节点可以是红或者黑
  • 红黑树不是高度平衡的,它的平衡是通过“自己的红黑规则”实现的
红黑规则
  1. 每一个节点或是红色的,或是黑色的
  2. 根节点必须是黑色
  3. 如果一个节点没有子节点或者父节点,则该节点相应的指针属性为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
  4. 如果某一个节点是红色的,那么它的子节点必须是黑色的(不能出现两个红色节点相连的情况)
  5. 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
    在这里插入图片描述
案例:成绩排序

​ 创建3个学生对象,属性为(姓名,语文成绩,数学成绩,英语成绩),按照总分从低到高输出到控制台;

import java.text.Collator;
import java.util.Locale;

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

    public Student() {
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", chinese=" + chinese +
                ", math=" + math +
                ", english=" + english +
                '}'+"总成绩:"+getSum();
    }

    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 getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public int getSum(){
        int sum = chinese + math + english;
        return sum;
    }

    @Override
    public int compareTo(Student o) {
        //按总成绩排序
        int result = this.getSum() - o.getSum();
        //总成绩相同,按语文成绩排序
        result = result == 0 ? this.chinese - o.chinese : result;
        //语文成绩也相同,按数学成绩排序
        result = result == 0 ? this.math - o.math : result;
        //语数成绩都相同,则英语成绩也相同,按姓名排序
        Collator col = Collator.getInstance(Locale.CHINESE);
        result = result == 0 ? col.compare(this.name,o.name) : result;
        return result;
    }
}
import java.util.TreeSet;

public class StudentTest {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<>();
        Student st1 = new Student("肖恩",90,90,80);
        Student st2 = new Student("阿衰",90,90,80);
        Student st3 = new Student("吉吉",100,100,100);
        ts.add(st1);
        ts.add(st2);
        ts.add(st3);
        System.out.println("姓名\t语文\t数学\t英语\t总分");
        for (Student t : ts) {
            System.out.println(t.getName()+"\t"+t.getChinese()+"\t\t"+t.getMath()+"\t\t"+t.getEnglish()+"\t\t"+t.getSum());
        }
    }
}

运行结果:
在这里插入图片描述

HashSet集合特点
  • 底层数据结构是哈希表
  • 不能保证存储和取出的数据完全一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以元素唯一

示例:

import java.util.HashSet;
import java.util.Iterator;

public class HashTest {
    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<>();
        hs.add("Hello");
        hs.add("world");
        hs.add("java");
        hs.add("java");
        hs.add("java");
        Iterator<String> it = hs.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("----------");
        for (String h : hs) {
            System.out.println(h);
        }
    }
}

运行结果:
在这里插入图片描述

哈希值

哈希值:是JDK根据对象的地址或者属性值,算出来的int类型的整数

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode():根据对象的地址计算出来的哈希值

对象的哈希值特点

  • 如果没有重写hashCode方法,那么是根据对象的地址值计算出来的哈希值;同一个对象多次调用hashCode()方法返回的哈希值是相同的,不同的对象的哈希值是不一样的。
  • 如果重写了hashCode方法,一般都是通过对象的属性值计算出哈希值;如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的。
案例:HashSet集合存储学生对象并遍历

创建一个HashSet集合,存储多个学生对象,并进行遍历;要求:学生对象的成员变量值相同,我们就认为是同一个对象;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    @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 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 +
                '}';
    }
}
import java.util.HashSet;

public class StudentTest {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<>();
        Student st1 = new Student("图图",8);
        Student st2 = new Student("图图",8);
        Student st3 = new Student("小美",7);
        hs.add(st1);
        hs.add(st2);
        hs.add(st3);
        System.out.println(hs);
    }
}

运行结果:
在这里插入图片描述

总结:如果HashSet集合要存储自定义对象,则要重写、hashCode和equals方法;

练习1

​ 假如你有3个室友,请使用HashSet集合保存3个室友的信息;信息如:小颖,18;小冰,20;小幂,19;

​ 室友以对象形式存在,包含姓名和年龄两个属性;使用代码保证集合中同名同年龄的对象只有一份;(相同姓名和年龄的对象认为是同一个对象)

要保证集合中同名同年龄的对象只有一份,要重写equals和hashCode方法

public class ShiYou {
    private String name;
    private int age;

    public ShiYou() {
    }

    public ShiYou(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ShiYou shiYou = (ShiYou) o;

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

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

    @Override
    public String toString() {
        return "ShiYou{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

测试类

import java.util.HashSet;

public class ShiYouTest {
    public static void main(String[] args) {
        HashSet<ShiYou> hs = new HashSet<>();
        ShiYou s1 = new ShiYou("小颖",18);
        ShiYou s2 = new ShiYou("小冰",20);
        ShiYou s3 = new ShiYou("小幂",19);
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        System.out.println(hs);
    }
}

运行结果:
在这里插入图片描述

Map
Map集合概述
  • Interface Map<K,V> K:键的数据类型;V:值的数据类型
  • 键不能重复,值可以重复
  • 键和值是一一对应的,每一个键只能找到自己对应的值
  • (键+值)这个整体我们称之为”键值对“或者”键值对对象“,在Java中叫做”Entry对象“

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap

示例:

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("2020001","花花");
        map.put("2020002","朵朵");
        map.put("2020003","图图");
        System.out.println(map); //{2020003=图图, 2020002=朵朵, 2020001=花花}
    }
}
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()集合的长度,也就是集合中键值对的个数
import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("2020001","花花");
        map.put("2020002","朵朵");
        map.put("2020003","图图");
        map.put("2020004","洛洛");
        map.put("2020005","香香");

        //如果要添加的键不存在,那么会把键值对都添加到集合中
        //如果要添加的键是存在的,那么会覆盖原先的值,把原先的值返回
        String s = map.put("2020005", "丁丁");
        System.out.println(s); //香香
        System.out.println(map); //{2020003=图图, 2020002=朵朵, 2020001=花花, 2020005=丁丁, 2020004=洛洛}

        String s1 = map.remove("2020005");
        System.out.println(s1); //丁丁
        System.out.println(map); //{2020003=图图, 2020002=朵朵, 2020001=花花, 2020004=洛洛}

        boolean b1 = map.containsKey("2020001");
        boolean b2 = map.containsKey("2020005");
        System.out.println(b1); //true
        System.out.println(b2); //false

        boolean b3 = map.containsValue("图图");
        boolean b4 = map.containsValue("香香");
        System.out.println(b3); //true
        System.out.println(b4); //false

        boolean empty1 = map.isEmpty();
        System.out.println(empty1); //false

        int size = map.size();
        System.out.println(size); //4

        map.clear();
        System.out.println(map); //{}

        boolean empty2 = map.isEmpty();
        System.out.println(empty2); //true
    }
}
Map集合的获取功能
方法名说明
Set keySet()获取所有键的集合
V get(Object key)根据键获取值
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合
K getKey()获得键
V getValue()获得值

Map集合的遍历方式一

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("2020001","花花");
        map.put("2020002","朵朵");
        map.put("2020003","图图");
        map.put("2020004","洛洛");
        map.put("2020005","香香");

        //获取到所有的键
        Set<String> keys = map.keySet();
        for (String key : keys) {
            //通过每一个键获取对应的值
            String value = map.get(key);
            System.out.println(key+"------"+value);
        }
    }
}

运行结果:
在这里插入图片描述

Map集合的遍历方式二

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("2020001","花花");
        map.put("2020002","朵朵");
        map.put("2020003","图图");
        map.put("2020004","洛洛");
        map.put("2020005","香香");

        //首先要获取到所有的键值对对象
        //Set集合中装的是键值对对象(Entry)对象
        //而Entry里面装的是键和值
        Set<Map.Entry<String,String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            //获取每一个键值对对象
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"---------"+value);
        }
    }
}

运行结果:
在这里插入图片描述

HashMap
  • HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法
案例:HashMap集合存储自定义对象并遍历

创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。存储三个键值对元素,并遍历;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + 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;
    }
}

测试类

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class StudentTest {
    public static void main(String[] args) {
        HashMap<Student,String> hm = new HashMap<>();
        Student st1 = new Student("小明",17);
        Student st2 = new Student("小樱",16);
        Student st3 = new Student("芝士",17);
        hm.put(st1,"上海");
        hm.put(st2,"北京");
        hm.put(st3,"南京");
        //先获取集合中所有的键,再根据键去获取对应的值
        Set<Student> key = hm.keySet();
        for (Student student : key) {
            String value = hm.get(student);
            System.out.println(student+"--------"+value);
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //先获取集合中所有的键值对,在获取键和对应的值
        Set<Map.Entry<Student, String>> entries = hm.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            System.out.println(entry.getKey()+"--------"+entry.getValue());
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        hm.forEach((Student key1,String value)->{
            System.out.println(key1+"--------"+value);
        });
    }
}

运行结果:
在这里插入图片描述

TreeMap
  • TreeMap底层是红黑树结构的
  • 依赖自然排序或比较器排序,对键进行排序
  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
案例

创建一个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 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() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        //按年龄排序
        int result = this.age - o.age;
        //若年龄相同按姓名排序
        result = result ==0 ? this.name.compareTo(o.name) : result;
        return result;
    }
}
import java.util.Comparator;
import java.util.TreeMap;

public class StudentTest {
    public static void main(String[] args) {
        //自然排序
        TreeMap<Student,String> tm = new TreeMap<>();
        Student st1 = new Student("小明",17);
        Student st2 = new Student("小樱",16);
        Student st3 = new Student("芝士",17);
        tm.put(st1,"上海");
        tm.put(st2,"北京");
        tm.put(st3,"南京");
        tm.forEach((Student key, String value) -> {
            System.out.println(key+"--------"+value);
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        //比较器排序
        TreeMap<Student,String> tm1 = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按年龄大小排序
                int result = o1.getAge() - o2.getAge();
                //年龄相同,按姓名排序
                result = result ==0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
        tm1.put(st1,"上海");
        tm1.put(st2,"北京");
        tm1.put(st3,"南京");
        tm1.forEach((Student key,String value) -> {
            System.out.println(key+"------"+value);
        });
    }
}

运行结果:
在这里插入图片描述

练习1

假如你有3个室友,请使用HashSet集合保存3个室友的信息;信息如下:路飞,18;漩涡鸣人,17;工藤新一,19;

要求:室友以对象形式存在,包含姓名和年龄两个属性;使用代码保证集合中同名同年龄的对象只有一份;(相同姓名和年龄的对象认为是同一个对象)

public class Shiyou {
    private String name;
    private int age;

    public Shiyou(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Shiyou() {
    }

    @Override
    public String toString() {
        return "Shiyou{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Shiyou shiyou = (Shiyou) o;

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

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

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

测试类:
import java.util.HashSet;

public class ShiyouTest {
    public static void main(String[] args) {
        HashSet<Shiyou> hs = new HashSet<>();
        Shiyou s1 = new Shiyou("蒙奇D-路飞",18);
        Shiyou s2 = new Shiyou("旋涡鸣人",17);
        Shiyou s3 = new Shiyou("工藤新一",19);
        Shiyou s4 = new Shiyou("旋涡鸣人",17);
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        //虽然s2和s4地址值不一样,但他们的内容一样,因为已经重写了equals和hashCode方法,所以不会重复添加
        System.out.println("我的室友是"+hs);
    }
}

运行结果:
在这里插入图片描述

练习2

请使用HashMap集合保存街道两旁的店铺名称;使用门牌号作为键,店铺名作为值,然后使用三种方式遍历输出;

信息如下:

2020001,小万面馆

2020002,小图粥馆

2020003,小蕊米馆

2020004,小福茶馆

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Lian2 {
    public static void main(String[] args) {
        HashMap<Integer,String> hm = new HashMap<>();
        hm.put(2020001,"小万面馆");
        hm.put(2020002,"小图粥馆");
        hm.put(2020003,"小蕊米馆");
        hm.put(2020004,"小福茶馆");
        //遍历方式一,先获取所有的键,再根据键找值
        Set<Integer> set = hm.keySet();
        for (Integer key : set) {
            System.out.println(key+"--"+hm.get(key));
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //遍历方式二,先获取所有的键值对对象在获取键值对中的键和值
        Set<Map.Entry<Integer, String>> entries = hm.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println(entry.getKey()+"--"+entry.getValue());
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //遍历方式三,接口中默认的forEach方法配合Lambda表达式
        hm.forEach((key,value)->{
            System.out.println(key+"--"+value);
        });
    }
}

运行结果:
在这里插入图片描述

练习3

请使用TreeMap集合保存学生信息,要求以学生对象为键,家庭住址为值,并按照学生的年龄从大到小排序后输出;

信息如下:

18岁的张三,北京

20岁的李四,上海

19岁的王五,天津

21岁的赵六,北京

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class StudentTest {
    public static void main(String[] args) {
        //注意如果没有让Student类实现自然排序接口,那么就需要创建TreeMap对象的时候,写自定义排序规则
        //且TreeMap集合并不依赖对象的hashCode方法和equals方法,因此学生类中hashCode方法和equals方法写不写都行;
        //但是如果是HashMap集合,就必须写;
        TreeMap<Student,String> tm = new TreeMap<>((s1,s2)->{
            //降序排列
            int result = s2.getAge() - s1.getAge(); //主要条件
            return result==0 ? s1.getName().compareTo(s2.getName()) : result; //次要条件
        });
        //创建对象添加信息
        Student s1 = new Student("张三",18);
        Student s2 = new Student("李四",20);
        Student s3 = new Student("王五",19);
        Student s4 = new Student("赵六",21);
        //添加入集合
        tm.put(s1,"北京");
        tm.put(s2,"上海");
        tm.put(s3,"天津");
        tm.put(s4,"北京");
        //遍历方式一
        Set<Student> set = tm.keySet();
        for (Student key : set) {
            因为重写了toString方法,所以可以直接打印  key
            System.out.println(key+"--"+tm.get(key));
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
        //遍历方式二
        Set<Map.Entry<Student, String>> entries = tm.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            System.out.println(entry.getKey()+"--"+entry.getValue());
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
        //遍历方式三
        tm.forEach((key,value)->{
            System.out.println(key+"--"+value);
        });
    }
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值