Day17.Set集合、Map集合

本文介绍了Set集合(HashSet、TreeSet)、Comparator比较器的使用,包括元素去重、排序原理及实际案例。深入探讨了哈希值、哈希表结构,以及如何在LinkedHashSet中保持有序性。
摘要由CSDN通过智能技术生成

1 Set集合

  1. 概述:该集合是一个单列集合,是Collection集合的子接口。
  2. 特点:
    (1)在java.util包中
    (2)该类元素特点:
                     不可重复:集合中不可以存储相同的元素(去重)
                     没有索引:集合中没有一些操作元素索引的方法
                     无序:元素存储的顺序和取出的顺序不能保持一致
  3. HashSet集合有两个实现类:HashSet 和TreeSet

代码

package demos1_treeset;

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

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

        Set<String> set = new HashSet<>();
        set.add("xyz");
        set.add("abc");
        set.add("123");
        set.add("123");
        set.add("早上好");

        Object[] obs = set.toArray();
        for (int i = 0; i < obs.length; i++) {
            String str = (String)obs[i];
            System.out.println(str);
        }

        String[] strs = new String[set.size()];
        set.toArray(strs);
        for (int i = 0; i < strs.length; i++) {
            String str = strs[i];
            System.out.println(str);
        }

        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            String str = it.next();
            System.out.println(str);
        }
        
        for(String str:set){
            System.out.println(str);
        }
    }
}

2 TreeSet集合

  1. 概述:TreeSet集合为Set集合的实现类。

  2. 构造方法:
    TreeSet():创建一个集合对象,对元素自然排序
    TreeSet(Comparator<? super E> comparator):使用Comparator比较器排序

  3. 特点:
    (1)集合不可以存放重复的元素(去重)
    (2)元素存入和取出的顺序不一致
    (3)元素是按照二叉树的方式存储元素,按照大小排列元素
    在这里插入图片描述

  4. 存储元素排序方式:
    (1)如果是一个基本数据类型对应的包装类型的元素,按照数据的大小,从小到达排列
    (2)如果是非中文字符串类型,则按照字母的顺序排序,从小到大排列
    (3)如果是中文字符串则按照中文字符解码后的数字大小,从小到大排列

    (4)如果是自定义的类型对象,不能直接使用TreeSet集合存储
    解决方案:实现Comparable接口,并重写接口中的compareTo方法

  5. 比较原理:
    (1)Comparable接口: 是一个比较接口
    (2)需要重写该接口的方法: public int compareTo(Object o)
    (3)比较规则:
                 返回值为0 – 相等 如果相等,就去重
                 返回值为正 将添加的参数存储在二叉树的左子树(先取)
                 返回值为负 将添加的参数存储在二叉树的右子树(后取)

代码

package demos1_treeset;

public class Person implements Comparable<Person>{
    private String name;
    private int age;

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

    public Person() {
    }

    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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Person o) {
       //规则:
        //如果方法的返回值为-1,默认添加的元素比较大,放在右子树
        //如果方法的返回值为1,默认添加的元素比较小,放在左子树
        //如果方法的返回值为0,默认将添加的元素视为重复的,就去重

        //想法1:按照年龄从小到大排列
        //this.age;//集合中已经存在的元素年龄(默认循环获取  二叉树中已经存在的元素)
        // o.age;//即将要添加的元素的年龄
//        return this.age-o.age;
        //想法2:按照年龄从大到小排列
//        return o.age-this.age;
        //想法3:按照名字(String)的默认排序方式(自然排序)排列
        //String类型已经默认实现Comparable接口,默认重写了compareto
//        return this.name.compareTo(o.name);
        //想法4:先按照年龄升序排列。如果年龄相同,再按照姓名默认排列
        int result1 = this.age-o.age;
        int result2 = this.name.compareTo(o.name);
        return result1 == 0? result2 :result1;
    }
}

3 Comparator比较器使用

  1. 概述:Comparator和Comparable一样都是用来比较数据的接口
  2. Comparable接口如果使用,需要被自定义类实现,且重写其中的compareTo方法
  3. Comparator接口如果使用,需要在创建TreeSet集合对象时传入一个该接口的实现类对象,且重写其中的compare方法
  4. 方法:compare(Object o1, Object o2):
    将参数o1当作集合中存在的数据,将参数o2当作需要添加的数据
  5. 排序的方式和Comparable接口的排序方式相同

代码

package demos1_treeset;

import java.util.Comparator;
import java.util.TreeSet;

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

        Comparator<Stu> com = new Comparator<Stu>() {
            @Override
            public int compare(Stu o1, Stu o2) {
                //方法中的两个参数:o1  o2
                //  o1:当做集合中已经存在的元素(底层默认循环获取)
                //  o2:当做即将要添加的元素即可

                //按照年龄降序排列
//               return o2.getAge()-o1.getAge();
                //先按照年龄升序,年龄相同,再按照姓名自然排序
                int result1 = o1.getAge()-o2.getAge();
                int result2 = o1.getName().compareTo(o2.getName());
                return result1 == 0?result2:result1;
            }
        };
        TreeSet<Stu> tr = new TreeSet<>(com);
        tr.add(new Stu("张三",23));
        tr.add(new Stu("李四",24));
        tr.add(new Stu("张三",24));
        tr.add(new Stu("王五",25));
        tr.add(new Stu("赵六",26));
        tr.add(new Stu("赵六",26));
        System.out.println(tr);
    }
}

3.1 案例

使用TreeSet集合存储多个学生信息,按照学生的总成绩升序排列
  如果总成绩相同,则按照语文成绩升序排列
  如果语文成绩也相同,则按照名字的自然排序升序排列

学生类:  
    属性:姓名  语文成绩   数学成绩

代码

package demos1_treeset;

import java.util.Comparator;
import java.util.TreeSet;

public class Demo05 {
//    使用TreeSet集合存储多个学生信息,按照学生的总成绩升序排列
//    如果总成绩相同,则按照语文成绩升序排列
//    如果语文成绩也相同,则按照名字的自然排序升序排列

    public static void main(String[] args) {
        Comparator<Student> com = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按照从成绩升序排列
                int result1 = o1.getSum() - o2.getSum();
                //按照语文成绩升序排列
                int result2 = o1.getChinese()-o2.getChinese();
                //按照名字自然排序
                int result3 = o1.getName().compareTo(o2.getName());
                //先按照总成绩,总成绩相同,再按照语文成绩,语文成绩相同,按照名字排序
                return result1 == 0 ? (result2 == 0 ? result3 : result2) : result1;
            }
        };

        TreeSet<Student> tr = new TreeSet<>(com);
        tr.add(new Student("张三",90,100));
        tr.add(new Student("李四",100,90));
        tr.add(new Student("王五",90,100));
        tr.add(new Student("赵六",80,110));
        tr.add(new Student("赵六",100,110));
        System.out.println(tr);

    }
}

4 HashSet集合

  1. 概念:属于set集合的实现类
  2. 特点:
    (1)无序,没有索引,不可重复
    (2)该集合没有特殊的方法,可以使用单列接口中定义的方法
    (3)该集合存储元素的方式,底层是根据哈希表来进行存储的

4.1 哈希值简介

  1. 是JDK根据对象的地址算出来的int类型的数值
  2. 如何获取哈希值
    Object类中的public int hashCode():返回对象的哈希码值
  3. 哈希值的特点
    (1)同一个对象多次调用hashCode()方法返回的哈希值是相同的
    (2)默认情况下,不同对象的哈希值不同,因为默认根据对象的地址值来获取
    而重写hashCode()方法,可以实现让不同对象的哈希值相同
  4. 特点总结:
    (1)两个对象的哈希值不同,肯定不是同一个对象
    (2)如果两个对象的哈希值一样,可能不是同一个对象

4.2 哈希表图示

  1. 哈希表:底层就是使用 数组 + 链表 结合产生的。
    JDK1.7版本哈希表的图示
    使用数组和链表组成
    在这里插入图片描述JDK1.8之后对哈希表的优化
    如果某一个链表上的元素个数>=8 会将当前链表转为一个红黑树(特殊二叉树)
    目的:降低链表的长度(让数据查询起来快速一点)

    如果链表上的元素被减少到6个,会再次将该红黑树转为普通的链表
    在这里插入图片描述

4.3 HashSet集合存储元素原理总结

  1. 要存储一个元素x,先获取x的哈希值,在将该哈希值进行处理,处理之后,获取一个 存储的位置
  2. 判断该位置是否有元素,如果该位置没有元素(就代表x的哈希值是唯一的),直接存入。
  3. 如果该位置有元素(集合中有元素的哈希值和x相同),就再次调用equals(重写)方 法比较这些元素的属性值
  4. 如果属性值也相同,就直接去重
  5. 如果属性值不相同,就存入到集合

代码

package demos2_hashset;

import java.util.HashSet;

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

        //默认:添加元素之前,获取当前元素的哈希值,如果哈希值不同,就直接存入(存入到指定的位置)
        //对开发而言:如果两个对象的属性值相同,就要实现去重

        //元素去重原理的总结:
        /**
         * 1、要存储一个元素x,先获取x的哈希值,在将该哈希值进行处理,处理之后,获取一个存储的位置
         * 2、判断该位置是否有元素,如果该位置没有元素,直接存入。(如果集合中没有元素的哈希值和x相同)
         * 3、如果该位置有元素(集合中有元素的哈希值和x相同),就再次调用equals(重写)方法比较哈希值相同的元素属性值
         * 4、如果属性值也相同,就直接去重
         * 5、如果属性值不相同,就存入到集合
         */

        HashSet<Person> hs = new HashSet<>();
        hs.add(new Person("张三",23));//1 hashcode
        hs.add(new Person("李四",24));//2 hashcode
        hs.add(new Person("王五",25));//3 hashcode
        hs.add(new Person("张三",23));//4 hashcode   4 equals
        hs.add(new Person("李四",24));//5 hashcode   5 eauals
        System.out.println(hs);
    }

    private static void test02() {
        HashSet<String> hs = new HashSet<>();
        hs.add("xyz");
        hs.add("xyz");
        hs.add("你好");
        hs.add("abc");
        System.out.println(hs);
    }

    private static void test01() {
        HashSet<Integer> hs = new HashSet<>();
        hs.add(555);
        hs.add(333);
        hs.add(444);
        hs.add(666);
        hs.add(111);
        System.out.println(hs);
    }
}

4.4 LinkedHashSet集合

  1. 概述:是一个单列集合,是HashSet集合的子类
  2. 特点:
    (1)没有特殊的方法,可以使用父类中继承的方法
    (2)元素特点:
                无索引
                不可重复:和父类去重的原理一样
                有序:可以保证元素存入的和取出的顺序

原因:每一个元素都会记录下一个存入元素的地址,在取出元素的时候,根据寻找地址的方式来寻找下一个元素。

4.5 单列集合特点总结:

  1. Collection接口:单列集合的顶层接口
                               只要是一个单列集合都是该接口的实现类或者子接口
                              该接口中定义的方法,任何一个单列集合都可以使用
  2. List:子接口
    特点:元素有序 有索引 可以重复存储
    有自己的特殊方法:通过索引操作元素
  3. ArrayList:
    List接口的实现类,底层数组实现,增删慢 查询块
  4. LinkedList:
    List接口的实现类,底层使用链表实现,增删快,查询慢
  5. Set:子接口
    特点:元素无序 没有索引 不可存储重复的元素(默认去重)
    没有自己特殊方法操作元素
  6. TreeSet:
        Set接口的实现类,底层使用二叉树实现
        无序去重:
             (1)元素实现Comparable接口,重写compareTo方法进行元素排序
             (2)给集合传入一个比较器对象Compartor接口的实现类对象,重写compare
  7. HashSet:
           Set接口的实现类 底层使用哈希表存储(数组+链表)
           无序方式:
                  根据要存储元素的哈希值来决定存在哪一个位置
           去重方式:
                  默认比较元素的哈希值和属性值
  8. LinkedHashSet:
    HashSet子类,特点:有序 去重 没有索引
    重点掌握:
          1、每一个集合的特点
          2、集合中常用的方法
          3、集合的遍历方式

5 Map集合

  1. 概念:
    现实生活中,我们常会看到这样的一种关系:IP地址与主机名,身份证号与个人, 用户名与密码,等等。这种一一对应的关系,就叫做映射。Java提供了专门的集合类用 来存放这种对象关系的数据,这个集合就是即Map接口。
  2. 特点:
    (1)该接口是双列集合顶层接口,不能直接创建对象
    (2)该类在java.util.包中,使用时需要导包
    (3)该集合中的每个元素是由一对数据组成,即:键值对
    键值对是由 键(key)和值(value)组成
    (4)键值对中的键是唯一的(不可重复),无序的
    值是可以不唯一的,一般是通过键来操作值

5.1Map集合中常用的方法

函数名解释
put(K key, V value)往集合中添加元素
remove(Object key)根据制定的key删除对应的键值对
remove(Object key, Object value)根据键和值删除对应键值对
clear()清空集合
containsKey(Object key)判断集合中是否包含key
containsValue(Object value)判断集合中是否包含value
get(Object key)根据key值获取对应的value值
isEmpty()判断集合是否为空
replace(K key, V value)替换key对应的value值
size()获取集合中键值对的对数

注意:
对于put函数来说
如果key值是第一次出现,就是添加
如果key值第二次出现,就是替换

代码

package demos3_map;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class Demo01 {
    public static void main(String[] args) {
        //创建一个双列集合对象
        Map<Integer,String> map = new HashMap<>();
        //在集合中添加键值对(映射关系的数据)
        map.put(666,"北京");
        map.put(111,"上海");
        map.put(333,"广州");
        map.put(444,"深圳");
        System.out.println(map);
        //根据key删除对应的键值对
        map.remove(111);
        //根据指定的键值对,删除对应的数据
        map.remove(666,"北京");
        System.out.println(map);
        //清空集合  键值对一并删除
//        map.clear();
//        System.out.println(map);
        //判断集合中是否包含该key
        System.out.println(map.containsKey(444));
        //判断集合中是否包含该value
        System.out.println(map.containsValue("深圳"));
        //根据key获取对应的value
        System.out.println(map.get(333));
        //不能反过来根据value获取key
        System.out.println(map.get("广州"));//null
        //判断集合是否是空
        System.out.println(map.isEmpty());
        //替换key对应的value
//        map.replace(444,"北京");
        //返回集合中键值对的对数
        System.out.println(map.size());
    }
}

6 Map集合的遍历

6.1 第一种遍历方式:通过key来获取值的方式【重点掌握】

  1. 先获取集合中的key到一个set集合中,遍历set集合获取每一个key,再通过key单独 拿到对应的value即可。
  2. 获取集合中的key到set集合中:keySet()
  3. 遍历set集合
  4. 通过get方法获取key值对应的value值
  5. 图示:
    在这里插入图片描述

代码

package demos3_map;

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

public class Demo02 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(111,"北京");
        map.put(222,"上海");
        map.put(333,"广州");
        map.put(444,"深圳");
        //将map中的每一个key获取到set集合中
        Set<Integer> set = map.keySet();
        //遍历set集合获取每一个key
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()){
            Integer key = it.next();
            String value = map.get(key);
            System.out.println("key:" + key + "...value:" + value);
        }
    }

    private static void test(Map<Integer, String> map, Set<Integer> set) {
        for(Integer key: set){
            //根据获取的key,再获取对应的value
            String value = map.get(key);
            System.out.println("key:" + key + "...value:" + value);
        }
    }
}

6.2 Map集合的第二种遍历方式:通过键值对对象获取键和值

  1. 先获取map集合中的键值对,获取的时候将键值对封装为一个个对象放到set集合中, 再遍历set集合获取每一对键值对对象,单独获取这一对数据的key和value.
  2. 获取键值对到set集合中:entrySet()
           获取的时候会将键值对,封装为一个个Entry对象,进行保存
           所以集合Set中保存的是一个个键值对对象(Entry对象)
           Entry:是Map集合中的一个内部接口
                    提供了两个方法:getKey()获取key
                                                getValue()获取value
  3. 遍历set集合,获取每一个entry对象
  4. 单独获取键值对对象中的key和value值:
         获取key值:entry接口中的getKey()
         获取value值:接口中的getValue()

代码

package demos3_map;

import java.util.*;
import java.util.Map.Entry;

public class Demo03 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(111,"北京");
        map.put(222,"上海");
        map.put(333,"广州");
        map.put(444,"深圳");
        //将键值对封装为一个个对象,保存在set集合中
        Set<Entry<Integer, String>> set = map.entrySet();
        //遍历set集合,获取每一个键值对对象
        Iterator<Entry<Integer, String>> it = set.iterator();
        while(it.hasNext()){
            Entry<Integer, String> en = it.next();
            Integer key = en.getKey();
            String value = en.getValue();
            System.out.println("key:" + key + "...value:" + value);
        }

    }

    private static void test(Set<Entry<Integer, String>> set) {
        for(Entry<Integer,String> en: set){
            //单独获取该对对象中的key和value
            Integer key = en.getKey();
            String value = en.getValue();
            System.out.println("key:" + key + "...value:" + value);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值