JavaSE学习笔记day15

零、 复习昨日

HashSet 不允许重复元素,无序

HashSet去重原理:

  • 先比较hashcode,如果hashcode不一致,直接存储
  • 如果hashcode值一样,再比较equals
  • 如果equals值为true,则认为完全一样,不存储即去重
  • 否则存储

如果使用的是空参构造创建出的TreeSet集合,那么它底层使用的就是自然排序,即放入TreeSet的元素都必须实现Comparable接口,重写方法compareTo

一、作业

  public static void main(String[] args) {
        String[] arr = {"a","b","c","a","b","c"};
        String[] strArr = setArray(arr);
        System.out.println(Arrays.toString(strArr ) );
    }

    // 第一题
    // 设计方法,将传入的数组去重后返回
    // 例如: 字符串数组:[“aa”,”bb”,”cc”,”aa”,”cc”,”bb”],将其去重变为[“aa”,”bb”,”cc”]
    public static String[] setArray(String[] arr) {

        // 遍历数组,取出一个,向set集合放一个,最后再变回数组
        HashSet<String> set = new HashSet<>( );
        for (String s : arr) {
            set.add(s);
        }
        String[] strArr = new String[set.size()];

        // 集合转数组
        String[] result = set.toArray(strArr);

        return result;
    }

注意: T[] toArray(T[] arr)

4 创建一个Teacher类 属性age name salary 
  创建10个Teacher对象 装入TreeSet中
  通过设置Teacher类 保证age大的在Treeset前面  
   age相同 
	salary小的在Treeset前面
    salary相同 name长度小的在Treeset前面
package com.qf.homework;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class Teacher implements Comparable<Teacher>{

    private int age;
    private String name;
    private double salary;
    
    // 省略部分代码...

    @Override
    public int compareTo(Teacher o) {
        // 如果年龄相同,则继续判断
        if(o.getAge() - this.age == 0){
            // 再判断工资,如果工资相同,则继续判断
            if (this.salary - o.getSalary() == 0) {
                // 判断名字长度,如果名字长度一致,要保留该人名,所以不能返回0,随意返回一个1或者-1
                // 如果名字长度不一致,名字短的在前
                return this.name.length() - o.getName().length() == 0 ? 1 : this.name.length() - o.getName().length();
            } else {
                // 如果工资不同,工资低的在前
                return this.salary - o.getSalary() > 0 ? 1 : -1;
            }
        } else {
            // 如果年龄不同,年龄大在前
            return o.getAge() - this.age;
        }
    }
}
    public static void main(String[] args) {

        TreeSet<Teacher> treeSet = new TreeSet<>( );
        treeSet.add(new Teacher(18,"老邢",50000));
        treeSet.add(new Teacher(21,"老邢",50000));
        treeSet.add(new Teacher(19,"小老邢",40000));
        treeSet.add(new Teacher(19,"老邢",40000));
        for (Teacher teacher : treeSet) {
            System.out.println(teacher );
        }
    }

二、比较器排序

TreeSet是会对元素进行排序去重,有两种实现方案

  • 使用空参构造方法创建出的TreeSet,底层使用自然排序,即元素要实现Comparable接口才能实现排序
  • 第二种方案: 可以使用有参构造,在创建TreeSet集合时,传入一个Comparator 比较器,这样存入的元素就会按照该比较器指定的排序方案排序( 不再使用默认的自然排序)

TreeSet(Comparator comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。

使用步骤

  • 自定义类实现Comparator 接口
  • 重写compar(T o1,T o2)方法
    • o1 就是之前compareTo方法中的this,即正在存储的元素
    • o2 就是之前compareTo方法中的o,即以前存储过的元素
    • 方法返回值与之前compareTo方法的返回值一样
      • 返回0 去重
      • 返回负数放左边
      • 返回正数放右边
  • 在创建TreeSet时,创建该比较器对象,传入TreeSet的构造方法

练习: 把昨天的排序练习使用比较器排序再写一遍

学生语数外总成绩排序题目改写

// 学生成绩类
public class StudentScore{

    private String name;
    private int chinese;
    private int math;
    private int english;
    // set get...
    // 有参 无参构造
}
// 自定义成绩比较器
public class MyScoreComparator implements Comparator<StudentScore> {
    @Override
    public int compare(StudentScore o1, StudentScore o2) {
        return o2.getTotal() - o1.getTotal() == 0 ? 1 : o2.getTotal() - o1.getTotal();

    }
}
    public static void main(String[] args) {
        // 创建集合时指定成绩比较器
        TreeSet<StudentScore> set2 = new TreeSet<>(new MyScoreComparator() );
        set2.add(new StudentScore("zhang3",70,70,70 ));
        set2.add(new StudentScore("wang5",100,100,100 ));
        set2.add(new StudentScore("li4",80,80,80 ));
        set2.add(new StudentScore("zhao6",60,60,60 ));
        set2.add(new StudentScore("zhou7",90,90,90 ));

        for (StudentScore score : set2) {
            System.out.println(score );
        }
    }

三、Collections

类似于与Arrays,Collections是集合的工具类,方法都是静态的

  • Collections.reverse(List list) 反转
  • Collections.shuffle(List list) 混洗
  • Collections.sort(List list) 排序
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>( );
        list.add(3);
        list.add(2);
        list.add(5);
        list.add(4);
        list.add(1);

        System.out.println(list );
        // 反转
        // Collections.reverse(list);
        //System.out.println(list );
        // 混洗
        //Collections.shuffle(list);
        // 排序,升序
        Collections.sort(list);
        System.out.println(list );

    }

四、Map

Map代表双列集合,一次存储一对键值对(K,V)
Map是接口,代表是键映射到值的对象,一个Map不能包含重复的键,值允许重复.每个键最多只能映射到一个值,即可以通过键找到值,但是不能通过值找键.

方法都是非常常见的方法,但是Map是接口无法演示

Map有两个常用实现类

  • HashMap
  • TreeMap

五、HashMap

HashMap是Map的实现类,现在JDK8及以后底层是由数组+链表+红黑树实现
并允许使用 null 值和 null

HashMap存储的元素是不保证迭代顺序,存储的键不允许重复,值允许重复


除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同


补充: Hashtable是线程安全的map集合,效率低 ; HashMap是线程不安全的,效率高
ConcurrentHashMap 即安全又高效的Map集合

HashMap的容量和扩容: 初始容量16,加载因子0.75 阈值是 16 * 0.75,达到阈值扩容至原来的2倍
ps: 昨天学习的HashSet所有特性,其实就是HashMap的特性,包括去重原理

5.1 方法演示

构造方法

HashMap()
构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity)
构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity, float loadFactor)
构造一个带指定初始容量和加载因子的空 HashMap。
HashMap(Map m)
构造一个映射关系与指定 Map 相同的新 HashMap。

方法

每个都很重要!!!

    public static void main(String[] args) {

        // 创建空的HashMap;
        HashMap<String,Integer> map = new HashMap<>();
        System.out.println(map );

        // 添加元素,一次添加一对,键值
        // put方法的返回值,如果该键之前没有映射值,返回null
        // 如果该键之前映射的有值,则将值覆盖,返回上次的旧值
        Integer v1 = map.put("a",1);
        System.out.println(v1 );

        Integer v2 = map.put("a", 2);
        System.out.println(v2 );

        Integer v3 = map.put("d", 4);
        System.out.println(v3 );
        map.put("b",2);
        System.out.println(map );

        // 取出元素
        // 通过键返回值
        Integer v = map.get("a");
        System.out.println(v );

        // 集合大小(元素个数)
        System.out.println(map.size() );

        // 集合是否为空
        System.out.println(map.isEmpty() );

        // 清空集合
        //map.clear();

        // 集合大小(元素个数)
        //System.out.println(map.size() );

        // 集合是否为空
        //System.out.println(map.isEmpty() );

        // 移除元素,根据键移除整个键值对,返回值
        Integer a = map.remove("a");
        System.out.println(a );

        System.out.println(map );
        
        
        /**
         *  boolean containsKey(Object key)
         *           判断集合中是否包含指定键,有则返回 true。
         *  boolean containsValue(Object value)
         *           判断集合中是否包含指定值,有则返回 true。
         */
        System.out.println(map.containsKey("A"));
        System.out.println(map.containsValue(11));
    }

5.2 迭代/遍历

Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容

  • Set keySet() 键集,返回一个Set集合,其中只有键
  • Collection values() 值集,返回一个Collection集合,其中只有值
  • Set> entrySet() 键值映射集,返回一个Set集合,其中放着key-value对象

5.2.1 键集

    public static void main(String[] args) {
        HashMap<String,Integer> map = new HashMap<>();
        map.put("a",1);
        map.put("b",2);
        map.put("c",3);
        map.put("d",4);

        // 键集遍历
        Set<String> keySet = map.keySet();
        // 获得迭代器
        Iterator<String> iterator = keySet.iterator( );
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("-----------" );
        for(String key : keySet) {
            System.out.println(key );
        }
    }

5.2.2 值集

        // 值集
        Collection<Integer> values = map.values();
        Iterator<Integer> iterator1 = values.iterator( );
        while (iterator1.hasNext( )) {
            System.out.println(iterator1.next());
        }
        System.out.println("-----------" );
        for (Integer value : values) {
            System.out.println(value );
        }

5.2.3 键值映射集 [非常重要]

Entry是Map接口中的内部接口,代表是一个键值对,即包含键和值.
且该Entry接口中提供了关于操作单个键,值的方法

  • K getKey()
  • V getValue()
      // 获得键值映射集合Set,其中放着Entry
        Set<Entry<String,Integer>> entrySet = map.entrySet();
        Iterator<Entry<String,Integer>> iterator2 =  entrySet.iterator();
        while (iterator2.hasNext( )) {
            // 从迭代器取出的是Entry
            Map.Entry<String,Integer> entry = iterator2.next();
            // 通过entry可以单独获得键,值
            String key = entry.getKey( );
            Integer value = entry.getValue( );
            System.out.println(key +"-->" + value );
        }

        // for循环
        for(Map.Entry<String,Integer> entry : entrySet) {
            System.out.println(entry.getKey() +"--->" +entry.getValue() );
        }

JavaSE学习笔记day15_第1张图片

5.3 去重原理

HashMap的去重其实就是昨天讲的HashSet的去重,因为HashSet底层就是HashMap

  1. 在创建HashSet时,其实在底层创建了HashMap

    image-20230224152026062
  2. 在向set中添加元素时,其实是向map的key上添加

    image-20230224152119890

所以HashMap的键的去重原理就是

  • 向键存储数据时,先调用键的hashcode()方法
  • 如果hashcode值不一样则直接存储
  • 如果hashcode值一样,再调用元素的equals()方法
  • 如果equals方法返回false,则存储
  • 如果equals方法返回true,则不存储

5.4 HashMap的应用

场景一: 适合有关联映射的场景

设计方法,传入字符串,输出该字符串中每个字符出现的次数,使用HashMap实现
例如: “abcHelloabcWorld”,输出 a出现2次,b出现2次,l出现3次,H出现1次

   /**
     * 倒推: a --> 3  b --> 2
     * @param str
     */
    public static void cishu(String str) {
        String[] strArr = str.split("");
        System.out.println(Arrays.toString( strArr) );

        HashMap<String, Integer> map = new HashMap<>( );

        for (int i = 0; i < strArr.length; i++) {
            String s = strArr[i];
            if (!map.containsKey(s)) {
                map.put(s,1);
            } else {
                Integer count = map.get(s);
                count++;
                map.put(s,count);
            }
        }

        // System.out.println(map );
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet( );
        for (Map.Entry<String,Integer> entry :entrySet) {
            String s = entry.getKey( );
            Integer count = entry.getValue( );
            System.out.println("字符"+s+",出现"+count+"次" );

        }
    }

场景二: Map可以当实体类对象

public class Student{
    private int age;
    private String name;
    // ...
    
}
Student s1 = new Student(18,"zs");
s1.getAge();
s1.getName();

使用Map模拟对象

HashMap<String, Object> stu = new HashMap<>( );
stu.put("age",18);
stu.put("name","zs");
stu.put("sex","男");

LinkedHashMap: …

六、TreeMap

TreeMap底层是红黑树(平衡二叉树的一种)
同样式存储键值对,键不允许重复且还会排序
默认是根据键元素的自然顺序排序或者,根据创建TreeMap时指定的Comparator比较器来排序

6.1 方法演示

方法详见api

   public static void main(String[] args) {

        TreeMap<String, Integer> map = new TreeMap<>( );
        map.put("b",2);
        map.put("e",5);
        map.put("a",1);
        map.put("a",2);
        map.put("d",4);
        map.put("c",3);

        // 其他正常的map方法...
        // 三个遍历方法...

        // 特殊的,有关于头尾操作的方法
        // 获得排序后的第一个
        String key = map.firstKey( );

        // 获得排序后的第一个Entry
        Map.Entry<String, Integer> entry = map.firstEntry( );
        System.out.println(entry );
    }

6.2 TreeMap排序去重原理

昨天学习的TreeSet的底层其实就是TreeMap

  • 创建TreeSet时,创建TreeMap

    image-20230224163057509
  • 向set集合添加元素时,其实是向TreeMap的键添加元素

    image-20230224163141732

即TreeMap的排序去重原理是什么?其实如果自然排序就是compareTo(),如果是比较器排序就是compar()方法

  • 方法返回0 去重
  • 方法返回负数 放在树左侧
  • 方法返回正数 放在树的右侧

七、总结

集合体系中最重要最常见的两个集合是ArrayList,HashMap

其他的,

  • 记住常用的API(crud和遍历)
  • 记住List和Set区别
  • 记住ArrayList和LinkedList区别
  • 记住HashMap扩容,去重原理
  • 记住TreeMap的排序去重原理

你可能感兴趣的:(javaSE-学习笔记,学习,java,算法)