Set和Map

Collections、Set、Map

// Collection总结
1. Collection
	是单列集合的根接口
	Collection
        |-- List
            |-- ArrayList
            |-- LinkedList
            |-- Vector
        |-- Set
            |-- HashSet
            |-- TreeSet
    add(E e)
    clear()
    remove(Object o)
    contains(E e)
    isEmpty()
    size()
    toArray()
    
2. Iterator
	Collection : iterator() -> 获取集合对应的迭代器(Iterator)
	Iterator :
		hasNext() : 判断是否有下一个元素
		next() : 获取下一个元素, 指针后移一位
		
	增强for循环
	for (元素的数据类型 变量名 : 容器) {}
3. 泛型
	作用: 用来限定集合中元素的数据类型
	好处:
		1. 将问题从运行时期提前到编译时期
		2. 省去了强转的麻烦
    泛型类
    泛型接口
    泛型方法
    	修饰符 <T> 返回值类型 方法名(T t) {}
    泛型的上下边界
    	<? extends E> : 泛型可以是E, 也可以是E的子类
    	<? super E> : 泛型可以是E, 也可以是E的父类
4. List
	特点: 有序, 有索引, 可以重复
	功能:
		add(int index, E e)
		remove(int index)
		set(int index, E e)
		get(int index)
		
    ArrayList : 底层是数组, 查询快, 增删慢
    LinkedList : 底层是链表, 查询慢, 增删快
    	addFirst(E e)
    	addLast(E e)
    	removeFirst()
    	removeLast()
    	getFirst()
    	getLast()
    	pop()
    	push()
    	
5. 常见的数据结构
	栈: 先进后出
	队列: 先进先出
	数组: 查询快, 增删慢
	链表: 查询慢, 增删快
    	
	

一. Set接口【理解】

List : 有序, 有索引, 可以重复

Set :

​ 无序 : 存和取的顺序不一致

​ 没有索引

​ 不可以重复(保证元素的唯一)

Set集合的学习重点, 放在学习如何保证元素的唯一

1. HashSet

  • 存储Java已经提供好的数据类型
  • 可以保证元素的唯一
HashSet<String> set = new HashSet<>();

set.add("潮汐海灵");
set.add("潮汐海灵");
set.add("丽桑卓");
set.add("丽桑卓");
set.add("劫");
set.add("劫");

System.out.println(set); //[潮汐海灵, 劫, 丽桑卓]

2. HashSet存储自定义类型

如果存入元素的hashCode()返回值是不同, 元素会直接存入集合

如何存入元素的hashCode()和集合中已有的hashCode()相同, 会调用equals()进行比较

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

        // 存放Hero类型的Set集合
        HashSet<Hero> set = new HashSet<>();

        // 添加元素
        set.add(new Hero("潮汐海灵", "大鲨鱼"));
        set.add(new Hero("潮汐海灵", "大鲨鱼"));
        set.add(new Hero("亚索", "哈萨key"));
        set.add(new Hero("亚索", "哈萨key"));
        set.add(new Hero("伊泽瑞尔", "精准弹幕"));
        set.add(new Hero("伊泽瑞尔", "精准弹幕"));

        // 遍历打印
        for (Hero hero : set) {
            System.out.println(hero); // 去重之后的结果
        }

    }
}
// =====================================================================
public class Hero {
    private String name;
    // 技能
    private String skill;

    public Hero() {
    }

    public Hero(String name, String skill) {
        this.name = name;
        this.skill = skill;
    }

    // get/set省略


    /*
        重写之后比较的是属性值
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Hero hero = (Hero) o;
        return Objects.equals(name, hero.name) &&
                Objects.equals(skill, hero.skill);
    }

    /*
        使用属性, 进行一系列的计算(为了属性不同, 让hashCode()一定不同), 最终得到一个值
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, skill);
    }

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

总结: 如果使用HashSet集合存储自定义对象, 想保证元素的唯一, 需要重写hashCode()和equals()方法

3. LinkedHashSet

LinkedHashSet是HashSet集合的子类

Set集合中惟一一个有序的集合

特点

  • 可以保证元素的唯一
  • 由于是链表, 可以保证有序(怎么存就怎么取)

4. TreeSet

TreeSet集合能够排序, 是因为存入集合的元素自身具备比较功能

特点

  • 可以保证元素的唯一
  • 可以对元素进行从小到大的排序(自然排序)

5. 二叉树

(1) 基本概念

  • 节点, 左子节点, 右子节点
  • 二叉树
  • 高度
  • 二叉查找树(二叉搜索树, 二叉排序树)
  • 平衡二叉树

(2) 左旋和右旋

  • 左旋: 根节点的右侧往左拉, 原右子节点变成新的根节点, 多余的左节点当做原根节点的右子节点

  • 右旋: 根节点的左侧往右拉, 原左子节点编程新的根节点, 多余的右节点当做原根节点的左子节点

详见资料中的图片

(3) TreeSet中的二叉树

二. Collections【会用】

用来操作集合的工具类

1. 可变参数

2. 常用功能

  • addAll
public static <T> boolean addAll(Collection<T> c, T... elements)
    将elements可变参数中的所有内容, 添加到c集合中
    
    由于形参定义的是Collection集合, 随意这个方法可以添加所有Colleciton的子类(ArrayList, HashSet ...)
  • shuffle
public static void shuffle(List<?> list) : 随机置换
    注意: 只能操作List集合, 不能操作Set集合
  • sort
public static <T extends Comparable<T>> void sort(List<T> list) :
	排序
    集合泛型的数据类型, 必须是Comparable的实现类
        
	Comparable: 自然排序 -> 从小到大的升序排序

3. 比较器

public static <T> void sort(List<T> list, Comparator<T> c)
   	

(1) 集合元素是Integer类型的排序

		ArrayList<Integer> list = new ArrayList<>();
		
		// 添加元素
		list.add(70);
        list.add(50);
        list.add(20);
        list.add(50);
        list.add(40);

        // 匿名内部类:  new 接口名() {}  ->  实现了该接口的实现类对象
        Collections.sort(list, new Comparator<Integer>() {
            /*
                返回值的作用
                    1 - 2 -> 升序
                    2 - 1 -> 降序
             */

            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });


        System.out.println(list);

(2) 集合元素是String类型的排序

package com.guang._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/*

    String字符串中比较器排序的使用

 */
public class Demo02 {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();

        Collections.addAll(list, "aaa", "b", "cc");

        // 自然排序: 首字母升序
        // Collections.sort(list);
        // 首字母降序
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // 字符串对象.compareTo(另一个字符串对象)
                // return o1.compareTo(o2); // 字典顺序升序
                // return o2.compareTo(o1); // 字典顺序降序

                // return o1.length() - o2.length(); // 长度升序
                return o2.length() - o1.length(); // 长度降序
            }
        });

        System.out.println(list);


    }
}

(3) 集合元素是自定义类型的简单排序

package com.guang._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

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

        ArrayList<Hero> list = new ArrayList<>();

        list.add(new Hero("a奈文摩尔", 1));
        list.add(new Hero("b雷克萨", 3));
        list.add(new Hero("b雷克萨", 4));
        list.add(new Hero("c奥蕾莉亚", 2));

        // 如果两种排序功能都存在, 使用比较器排序
        // 不报错: Integer, String
        Collections.sort(list, new Comparator<Hero>() {
            @Override
            public int compare(Hero o1, Hero o2) {
                return o2.getName().compareTo(o1.getName());
            }
        });

        System.out.println(list);

    }
}

(4) 集合元素是自定义类型的进阶排序

package com.guang._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*

    先按照姓名降序, 如果姓名相同, 按照排名升序

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

        ArrayList<Hero> list = new ArrayList<>();

        list.add(new Hero("a奈文摩尔", 1));
        list.add(new Hero("b雷克萨", 4));
        list.add(new Hero("b雷克萨", 2));
        list.add(new Hero("b雷克萨", 3));
        list.add(new Hero("b雷克萨", 1));
        list.add(new Hero("c奥蕾莉亚", 2));

        Collections.sort(list, new Comparator<Hero>() {
            @Override
            public int compare(Hero o1, Hero o2) {
                // 如果姓名相同, 比较rank
                // o2.getName().compareTo(o1.getName() 返回 0, 说明姓名相同
                int num = o2.getName().compareTo(o1.getName());
                /*if (num == 0) {
                    return o1.getRank() - o2.getRank();
                } else { // 姓名不同
                    return num;
                }*/

                return num == 0 ? o1.getRank() - o2.getRank() : num;
            }
        });

        System.out.println(list);

    }
}

(5) TreeSet也可以使用比较器

public TreeSet(Comparator<? super E> comparator)

有Studnet类 [name:String , age:int, math:int, english:int, chinese:int, totalScore:int]

  1. 先按照总成绩降序排序
  2. 总成绩相同, 按照语文降序
  3. 总成绩和语文都相同, 按照数学降序
  4. 总成绩, 语文, 数学相同, 按照年龄降序

对应的练习demo案例:

1、创建对象:

public class Student {
    private String name;
    private Integer age;
    private Integer math;
    private Integer english;
    private Integer chinese;
    private Integer totalScore;
    // 无参、有参、getter/setter和toString方法省略(注意修改一个getTotal的值)
}    

测试代码:

public class StudentDemo {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("1", 22, 66, 33, 76, 56));
        studentList.add(new Student("2", 11, 16, 77, 88, 99));
        studentList.add(new Student("3", 9, 82, 99, 99, 34));
        studentList.add(new Student("4", 17, 33, 77, 88, 99));
        // 这里来使用函数式接口比较好
        Collections.sort(studentList, (s1, s2) -> {
            // s1-s2----->升序
            // s2-s1----->降序
            /**
             * 1. 先按照总成绩降序排序
             * 2. 总成绩相同, 按照语文降序
             * 3. 总成绩和语文都相同, 按照数学降序
             * 4. 总成绩, 语文, 数学相同, 按照年龄降序
             */
            int var1 = s2.getTotalScore() - s1.getTotalScore();
            int var2 = var1 == 0 ? s2.getChinese() - s1.getChinese() : var1;
            int var3 = var2 == 0 ? s2.getMath() - s1.getMath() : var2;
            int var4 = var3 == 0 ? s2.getAge() - s1.getAge() : var3;
            return var4;
        });
        for (Student student : studentList) {
            System.out.println(student);
        }
        /**
         * Student{name='3', age=9, math=82, english=99, chinese=99, totalScore=280}
         * Student{name='4', age=17, math=33, english=77, chinese=88, totalScore=198}
         * Student{name='2', age=11, math=16, english=77, chinese=88, totalScore=181}
         * Student{name='1', age=22, math=66, english=33, chinese=76, totalScore=175}
         */
    }
}

注意看一下:

math的成绩成立的前提之下,是按照总成绩、语文降序的前提之下进行排序的;

age是在总成绩、语文、数学分数相同的前提下,进行排序的;

三. Map集合【重点】

1. Map集合的特点

  • 将键映射到值的对象, 双列集合, 以键值对的形式存在
  • Map集合中的键是唯一的
  • 一个键最多对应一个值(可能没有值(null), 一个值)

键值对形式存在

键是唯一的

2. 双列集合的继承体系

3. Map中的常用方法

Map<K,V>
K : key 键
V : value 值

(1) 增删改查

  • 增加/修改
V put(K key, V value) : 向集合中添加一组键值对, 
						如果键相同, 值覆盖
                            
      返回值: 返回的是替换掉的值
  • 删除
V remove(Object key) : 删除指定键对应的, 键值对
      返回值: 返回被删除掉的值
  • 查询
V get(Object key) : 根据键, 获取值

(2) 其他操作

  • 长度
int size() : 获取集合中, 键值对 的个数
  • 判断是否包含
boolean containsKey(Object key) : 判断集合中是否包含指定键
boolean containsValue(Object value) : 判断集合中是否包含指定值
  • 获取所有键
Set<K> keySet() : 获取Map集合中所有键存在的Set集合
    
    返回值: 返回存放所有键的Set集合
  • 获取所有值
Collection<V> values() : 获取Map集合中所有值存在的Colletion集合

4. Map集合的遍历

(1) 键找值

  1. 获取所有的键
  2. 通过键获取值
        HashMap<String, String> map = new HashMap<>();

        map.put("C罗", "葡萄牙");
        map.put("梅西", "阿根廷");
        map.put("德布劳内", "比利时");
        map.put("内马尔", "巴西");
        map.put("武磊", "中国");

        // 获取所有键 -> Set<K>
        Set<String> keySet = map.keySet();
        // 遍历Set集合, 获取每一个键
        for (String key : keySet) {
            // 根据键获取值
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }

(2) 键值对

Set<Map.Entry<K,V>> entrySet() : 获取所有键值对 所在的 Set集合
Set<键值对>
        HashMap<String, String> map = new HashMap<>();

        map.put("C罗", "葡萄牙");
        map.put("梅西", "阿根廷");
        map.put("德布劳内", "比利时");
        map.put("内马尔", "巴西");
        map.put("武磊", "中国");

        // Set集合存放String -> Set<String>
        // Set集合中存放的是  键值对
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        // 遍历Set集合, 获取每一个键值对
        // Map.Entry<String, String> -> 键值对
        for (Map.Entry<String, String> entry : entrySet) {
            // 键值对类型中有两个功能: getKey(), getValue()
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }

5. HashMap存储自定义类型

如果HashMap存储自定义对象作为键, 想保证键的唯一, 需要重写hashCode()和equals()

6. LinkedHashMap

特点

  • 保证键的唯一
  • 有序(怎么存就怎么取)

7. TreeMap

特点

  • 保证键的唯一
  • 可以根据键进行排序

8. 集合的嵌套

  • ArrayList集合嵌套HashMap
package com.guang._04map;

import java.util.ArrayList;
import java.util.HashMap;

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

        // 创建ArrayList集合, 集合中存放HashMap
        ArrayList<HashMap<String , String>> list = new ArrayList<>();

        // 创建HashMap
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("姓名", "张三");
        map1.put("年龄", "23");
        map1.put("性别", "男");

        HashMap<String, String> map2 = new HashMap<>();
        map2.put("姓名", "李四");
        map2.put("年龄", "24");
        map2.put("性别", "女");

        HashMap<String, String> map3 = new HashMap<>();
        map3.put("姓名", "王五");
        map3.put("年龄", "25");
        map3.put("性别", "男");

        // 将Map集合添加到List中
        list.add(map1);
        list.add(map2);
        list.add(map3);

        // 遍历List集合, 获取到每一个元素, 元素是HashMap类型
        for (HashMap<String, String> map : list) {
            System.out.println("~~~~~~~~~~~~~");
            // 遍历HashMap
            for (String key : map.keySet()) {
                System.out.println(key + "=" + map.get(key));
            }
        }
    }
}

9. Map集合的练习

  • 需求: 键盘录入一个字符串, 获取字符串中每一个字符出现的次数
  • 键盘录入: abcdabcdab -> a(3)b(3)c(2)d(2)
public class MapTest {
    public static void main(String[] args) {
        Map<Character,Integer> stringIntegerMap = new HashMap<>();
        String tmp = "adfsafcafdsafdsa";
        char[] chars = tmp.toCharArray();
        for (char var0 : chars) {
            stringIntegerMap.put(var0,!stringIntegerMap.containsKey(var0)?1:stringIntegerMap.get(var0)+1);
        }
        // 遍历输出
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<Character, Integer> var1 : stringIntegerMap.entrySet()) {
            Character var2 = var1.getKey();
            Integer var3 = var1.getValue();
            stringBuilder.append(var2+"("+var3+")");
        }
        System.out.println(stringBuilder);
    }
}

四. 总结

很多set的底层都是给予map来进行实现的。所以这部分很有必要来对齐进行讲清楚。

set的最大特性是为了保持有序,而map的特性是给予引用类型重写的hashCode和equals方法来进行实现的。

posted @ 2022-02-27 18:17  写的代码很烂  阅读(29)  评论(0编辑  收藏  举报