Java两周半速成之路(第十二天)

news2024/10/3 8:30:18

一.泛型(JDK1.5以后出现的机制)

1.泛型由来

为什么会有泛型呢? 通过案例引入 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。

演示:

import java.util.ArrayList;
import java.util.Iterator;

/*
    在此之前的集合,我们使用的时候,可以传入不同的数据类型的元素。
    但是,实际开发中,一个集合只能够存储一种数据类型,为了就是将来获取元素处理的时候,处理方式能够统一
    想一想之前也学习过一种容器,这种容器在定义的时候,就明确了元素的数据类型(数组)
    现在集合想要模仿使用定义数组时的特点,可以明确一个集合元素的数据类型。
    java为了实现这样的功能,提供了一个技术给我们使用:泛型

    语句定义格式:<引用数据类型>
    泛型的好处:
        1、消除了程序中大部分的黄色警告
        2、在获取元素的时候,不需要做向下转型
        3、限制了集合存储的数据类型,将来处理的方式统一

 */
public class ArrayListDemo1 {
    public static void main(String[] args) {
//        //创建集合对象
//        ArrayList list = new ArrayList();
//
//        //创建元素对象并添加到集合中
//        list.add("java");
//        list.add("hadoop");
//        list.add("hive");
//        list.add("java");
//        list.add("hbase");
        list.add(12);
//
//        //迭代器遍历
//        Iterator iterator = list.iterator();
//        while (iterator.hasNext()) {
//            String s = (String) iterator.next();
//            System.out.println(s + "--" + s.length());
//        }


        //加入了泛型创建集合对象
        //  ArrayList<E>
        ArrayList<String> list2 = new ArrayList<>(); // 后面的尖括号泛型中的数据类型,根据前面定义时的泛型自动类型推断
        // String[] arr;


        //创建元素对象并添加到集合中
        list2.add("java");
        list2.add("hadoop");
        list2.add("hive");
        list2.add("java");
        list2.add("hbase");
//        list2.add(12);

        //迭代器遍历
        Iterator<String> iterator2 = list2.iterator();
        while (iterator2.hasNext()) {
            String s = iterator2.next();
            System.out.println(s + "--" + s.length());
        }

    }
}

2.泛型应用

泛型类

把泛型定义在类上

格式: public class 类名<泛型类型1,…>

注意:泛型类型必须是引用类型

演示:

/*
    开发代码的时候,写的泛型,将来使用的时候,应该传入一个引用数据类型给到开发时的泛型中接收
    将类当作参数一样进行传递
    <>中应该定义一个变量,用于接收将来使用时传入的引用数据类型,有点类似于形式参数
    就要符合标识符的命名规则
    如果泛型中只需要接收一个引用数据类型,定义时,只需要写一个大写的字母
 */
class Demo2<E> {      // Demo2<Integer>
    //E在一个类中是唯一的
    public void fun(E a) {    //Integer
        System.out.println(a);
    }

}

/*
    泛型类:在开发代码的时候,将泛型写在类上
 */
public class FanXingDemo2 {
    public static void main(String[] args) {
        Demo2<Integer> demo2 = new Demo2<>();
        demo2.fun(11);
    }
}

泛型方法

把泛型定义在方法上

格式:public <泛型类型> 返回类型 方法名(泛型类型 ...)

演示:

class Demo3 {

    //泛型方法,将来调用时可以传入任意的数据类型,与类上面的泛型无关
    public <E> void fun3(E a) {
        System.out.println(a);
    }
}

/*
    泛型方法:将泛型定义在方法上
 */
public class FanXingDemo3 {
    public static void main(String[] args) {
        Demo3 s3 = new Demo3();
        s3.fun3(11);
        s3.fun3('s');
    }
}

泛型接口

把泛型定义在接口上

格式:public  interface 接口名<泛型类型1…>

import java.util.ArrayList;
import java.util.List;

//如果一个类实现一个接口,这个接口有泛型的话,类定义时,也要有泛型,一般情况下与接口的泛型写法一致
interface Inter<E> {
    void fun4(E a);
}

class InterImtl<E> implements Inter<E> {
    @Override
    public void fun4(E a) {
        System.out.println(a);
    }
}

public class FanXingDemo4 {
    public static void main(String[] args) {
        Inter<String> inter = new InterImtl<>();
        inter.fun4("xuyou");
//        inter.fun4(23);

        List<String> list1 = new ArrayList<>();
        list1.add("12");
        System.out.println(list1);
//        list1.add(12);
    }
}

3.泛型通配符

第一种:

<?>                     任意类型,如果没有明确,那么就是Object以及任意的Java类

第二种:

? extends E        向下限定,E及其子类

第三种:

? super E            向上限定,E及其父类

演示:

import java.util.ArrayList;
import java.util.Collection;

/*
    开发程序时,泛型的高级用法:
        泛型通配符<?>
            任意类型,如果没有明确,那么就是Object以及任意的Java类了
        ? extends E
            向下限定,E及其子类
        ? super E
            向上限定,E及其父类
 */
class Animal {
    public void fun() {

    }
}

class Dog extends Animal {
}

class Cat extends Animal {

}

class Demo5<E> {    E: Animal

    public void fun(ArrayList<? super E> e) {
        System.out.println(e);

    }
}

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

        // 向下限定,E及其子类
        ArrayList<? extends Animal> list1 = new ArrayList<Animal>();
        ArrayList<? extends Animal> list2 = new ArrayList<Dog>();
        ArrayList<? extends Animal> list3 = new ArrayList<Cat>();
//        ArrayList<? extends Animal> list4 = new ArrayList<Object>();

        //E: Animal
        // ? super E 向上限定
        Demo5<Animal> d = new Demo5<>();
        //ddAll(Collection<? extends E> c)
        // Collection<? extends E> 表示将来应该传入一个Collection集合对象,并且集合中的元素类型是E本身或者是E的子类类型
        // E: Animal

        ArrayList<Animal> list4 = new ArrayList<>();
        ArrayList<Dog> list5 = new ArrayList<>();
        ArrayList<Cat> list6 = new ArrayList<>();
        ArrayList<Object> list7 = new ArrayList<>();
        
        d.fun(list4);
        d.fun(list7);
//        d.fun(list5);
        
    }
}

 4.常用类型:

E:代表元素(Element)类型,通常在集合中使用,如 List<E>。
K:代表键(Key)的类型,通常在 Map 中使用,如 Map<K, V>。
V:代表值(Value)的类型,通常在 Map 中使用,如 Map<K, V>。
T:代表任意类型(Type),通常在方法中使用,如 public <T> T method(T obj)。
N:代表数字类型,如 Number 类型。
R:代表返回类型,如 public <T> R method(T obj)

二. 增强for循环

1.概述

(1)是用于遍历Collection集合和数组

(2)增强for循环存在的目的,主要适用于替代Collection中的迭代器的。

2.格式

        for(元素数据类型 变量 : 数组或者Collection集合) {   

                        语句体 (使用变量即可,该变量就是元素)

    }

3.注意事项:

                       增强for的目标要判断是否为null

演示:


import java.util.ArrayList;
import java.util.Objects;

/*
    增强for循环:是用于遍历Collection集合和数组的

    语句定义格式:
        for(元素数据类型 变量名 : Collection集合/数组){
           直接使用变量名;
        }

    增强for循环存在的目的,主要适用于替代Collection中的迭代器的。

 */
public class Demo1 {
    public static void main(String[] args) {
        //遍历数组
//        int [] arr = null;
        int [] arr ={11,22,33,44,55};
        for (int i : arr) {
            System.out.println(i);
        }
        System.out.println("+++++++++++++++++++++++++++++++++++++++++++");
        //遍历集合
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("nihao");
        list1.add("xuyou");
        list1.add("caozei");
        list1.add("11");

        for (String s : list1) {
            System.out.println(s);
        }
    }
}

三.静态导入

1.概述:

(1)格式:import static 包名….类名.方法名;

(2)可以直接导入到方法的级别

2.注意事项

(1)方法必须是静态的

(2)如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。

四.可变参数

1.概述:指的是方法将来调用时,可以传入若干个指定类型的参数

2.使用场景:定义方法的时候不知道该定义多少个参数时使用

3.注意:

(1)一个方法定义中只能有一个可变参数

(2)如果一个方法有多个参数和可变参数,那么可变参数要最后一个定义

演示:

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

/*
     可变参数:指的是方法将来调用时,可以传入若干个指定类型的参数

     注意:
         1、一个方法定义中只能有一个可变参数
         2、可变参数必须在参数列表中的最后一个定义
  */
public class KeBianDemo {
    public static void main(String[] args) {

        //需求1:定义一个方法,求四个int类型值的和
        System.out.println(getSum(1, 2, 3, 4));
        //需求2:定义一个方法,传入姓名以及各科分数,求总成绩
        getSum("小明", 11, 22, 33, 44);

    }
    
    //将来传入若干个int类型的元素值,JVM将其封装到一个数组中,这个数组名叫做arr
    public static int getSum(int... arr) {
        int scoreSum = 0;
        for (int i : arr) {
            scoreSum += i;
        }
        return scoreSum;
    }

    public static void getSum(String name, int... arr) {
        int scoreSum = 0;
        for (int i : arr) {
            scoreSum += i;
        }
        System.out.println(name + " " + scoreSum);
    }

}

五. List集合练习

1.集合的嵌套遍历

import java.util.ArrayList;

/*
    集合的嵌套遍历
        学校(集合),目前有4个班级 1,2,3
        每个班级也是一个集合 ArrayList<Student> class = new ArrayList<>();
 */
class Student {
    String name;
    int age;

    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 "name:" + name + ",age:" + age;
    }
}

public class ListTest1 {
    public static void main(String[] args) {
        ArrayList<Student> class1 = new ArrayList<>();
        class1.add(new Student("小1", 18));
        class1.add(new Student("小2", 14));

        ArrayList<Student> class2 = new ArrayList<>();
        class1.add(new Student("小3", 16));
        class1.add(new Student("小4", 18));

        ArrayList<Student> class3 = new ArrayList<>();
        class1.add(new Student("小5", 13));
        class1.add(new Student("小6", 17));
        class1.add(new Student("小7", 21));

        ArrayList<ArrayList<Student>> school = new ArrayList<>();
        school.add(class1);
        school.add(class2);
        school.add(class3);


        for (ArrayList<Student> clazz : school) {
            for (Student student : clazz) {
                System.out.println(student);
            }
        }
    }
}

2.获取10个1-20之间的随机数,要求不能重复  

import java.util.ArrayList;
import java.util.Random;


//2.获取10个1-20之间的随机数,要求不能重复
public class ListDemo2 {
    public static void main(String[] args) {
        ArrayList<Integer> numberLIst = new ArrayList<>();
        Random random = new Random();

        while (numberLIst.size() < 10) {
            int number = random.nextInt(20)+1;
            if (!numberLIst.contains(number)) {
                numberLIst.add(number);
            }
        }
        System.out.println(numberLIst);
    }
}

3.键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

/*
    键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
 */
public class LIstTest3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        ArrayList<Integer> list = new ArrayList<>();
        int number = -1;
        int i = 1;
        while (number!=0) {
            System.out.print("请输入第" + i + "个数据:");
            i++;
            int number1 = sc.nextInt();
            if(number1==0){
                break;
            }
            list.add(number1);
        }

        //方式一:
        //将集合中的第一个元素默认为最大值
        Integer maxNumber = list.get(0);
        for (Integer num : list) {
            if (num>maxNumber){
                maxNumber=num;
            }
        }
        System.out.println("最大值为:"+maxNumber);

        //方式二:利用Collecyions工具类
//        System.out.println("最大值为:"+Collections.max(list));
    }
}

六.Set接口及其子类HashSet

1.概述

Set(接口) :元素无序且唯一             (无序:存储和取出的顺序不一致)

           --HashSet :   底层数据结构是哈希表,线程不安全的,效率高

2.HashSet

(1)构造方法:

HashSet()         创建一个空的集合

(2)注意:

(1)HashMap实例具有默认初始容量(16)和负载因子(0.75)

(2)HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法

演示:

import java.util.HashSet;
import java.util.Objects;

/*
    使用HashSet存储学生对象,当学生对象的姓名和年龄一样的时候,表示是同一个学生,应该要进行去重

    结论:
        HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法。
 */
class Student {
    String name;
    int age;

    public Student() {
    }

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

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class HashSetDemo {
    public static void main(String[] args) {
        //创建一个HashSet集合
        HashSet<Student>  studentSet= new HashSet<>();
        //创建学生对象studentSet
        Student student1 = new Student("小明", 12);
        Student student2 = new Student("小花", 14);
        Student student3 = new Student("小二", 18);
        Student student4 = new Student("小李", 22);
        Student student5 = new Student("小明", 12);
        //将元素添加到HashSet集合中
        studentSet.add(student1);
        studentSet.add(student2);
        studentSet.add(student3);
        studentSet.add(student4);
        studentSet.add(student5);
        //遍历集合
        for (Student student : studentSet) {         //此时遍历后的结果并没有去重,原因是底层源码中没有重写equals和hashCode方法,比较的是地址值
            System.out.println(student);
        }

    }
}

 HashSet中的add()方法源码分析:


public class HashSet<E> implements Set<E>{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }

    //set1.add("pitaya");
    public boolean add(E e) {
        //HashSet中的add方法底层是调用了HashMap中的put方法
        // e -- student1
        // PRESENT -- new Object()
        return map.put(e, PRESENT)==null;
    }
}


public class HashMap<K,V> implements Map<K,V>{
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public V put(K key, V value) {
        // key -- "pitaya"
        // value -- new Object()
        // 与元素类型中的hashCode方法有关
        return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node<K,V>[] tab;
        Node<K,V> p;
        int n, i;
        //第一次初始化一个hashTable出来
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //第一个元素,直接放入到哈希表中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e;
            K k;
            //判断待插入的元素与已经在哈希表中的元素
            //1、哈希值是否一样 元素类.hashCode()方法
            //2、内容值是否一样 元素类.equals()方法
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

}

3. LinkedHashSet类

1.概述:

(1)为HashSet的子类,底层数据结构是哈希表与双向链表

(2)元素有序唯一,由链表保证元素有序,由哈希表保证元素唯一

2.演示:

/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
 */
import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //创建LinkedHashSet集合
        LinkedHashSet<Integer> set = new LinkedHashSet<>();
        //添加元素
        set.add(11);
        set.add(2);
        set.add(33);
        set.add(11);
        set.add(23);
        //遍历
        for (Integer i : set) {
            System.out.println(i);
        }
    }
}

 4.TreeSet类

1.概述:

是set接口实现的具体子类

2.排序方式:

(1)使用元素的自然顺序Comparable<T>对元素进行排序(自然排序)

(2)根据创建 set 时提供的Comparator进行排序(比较器排序)

  如何使用这两种排序具体取决于使用的构造方法

3.TreeSet是如何保证元素的排序和唯一性的:

底层数据结构是红黑树(红黑树是一种自平衡的二叉树)

4.构造方法

5.自然排序:

(1)演示:


import java.util.TreeSet;

/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
            -- TreeSet 底层数据结构是红黑树(可排序的),线程不安全,效率高

    TreeSet中的排序方式有两种:
        1、自然排序  Comparable
        2、比较器排序 comparator
    需求1:使用TreeSet存储字符串对象,并遍历

    通过观察源码发现,因为我们创建TreeSet集合对象的时候,使用的是无参构造方法
    所以底层创建TreeMap的时候也是无参构造方法,comparator是null,不走比较器排序
    走的是自然排序
 */
public class TreeSetDemo1 {
    public static void main(String[] args) {
        //创建TreeSet集合
        TreeSet<Integer> treeSet = new TreeSet<>();
        //添加元素
        treeSet.add(11);
        treeSet.add(22);
        treeSet.add(87);
        treeSet.add(43);
        treeSet.add(98);
        //遍历
        for (Integer i : treeSet) {
            System.out.println(i);        //元素从小到大排序
        }
    }
}

 (2)实例:创建学生对象,加入到TreeSet集合中,按照年龄从小到大排序输出,要求使用自然排序

演示:

import java.util.TreeSet;

/*
    使用TreeSet集合存储自定义对象Student2(String name,int age)
    要求:最终要按照年龄从小到大排序,并且当学生的姓名和年龄一样的时候要进行去重

    我们按照存储字符串一样的逻辑存储自定义对象,运行的时候报错了
    ClassCastException:
    com.shujia.day13.Student2 cannot be cast to java.lang.Comparable
    原因是因为,我们现在使用的时候自然排序方式,需要元素的类实现Comparable接口,才可以进行转型

 */
class Student implements Comparable<Student> {
    String name;
    int age;

    public Student() {
    }

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

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

    @Override
    public int compareTo(Student o) {
        //显式条件:按照年龄从大到小排序
        //this -- 待插入的元素
        //o -- 已经在树中的根
        int cha = o.age - this.age;
        return (cha == 0) ? o.name.compareTo(this.name):cha;
    }
}


public class TreeSetDemo2 {
    public static void main(String[] args) {
        //创建集合
        TreeSet<Student> treeSet = new TreeSet<>();
        //新建学生对象
        Student student1 = new Student("小一", 9);
        Student student2 = new Student("小二", 34);
        Student student3 = new Student("小三", 12);
        Student student4 = new Student("小四", 6);
        Student student5 = new Student("小五", 9);
        //添加元素
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        treeSet.add(student5);

        //遍历
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}

上述代码Student类如果没有实现Comparable<>接口,就会报以下错误,原因是我们现在使用的时候自然排序方式,需要元素的类实现Comparable<>接口,才可以进行转型

注意: 重写Comparable<>接口中的compareTo()方法,可以让我们实现元素的排序问题

6.比较器排序:

 在创建TreeSet对象的时候使用匿名内部类Comparator重写compareTo()方法

*
    比较器排序:
        public TreeSet(Comparator<? super E> comparator) {
            this(new TreeMap<>(comparator));
        }

    小总结:
        1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法,
            但是要保证元素的类实现Comparable接口,重写compareTo方法。
        2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法,
            传入一个实现了Comparator接口的子类对象,重写compare方法
 */
import java.util.Comparator;
import java.util.TreeSet;

class Student1 {
    String name;
    int age;

    public Student1() {
    }

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

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

}


public class TreeSetDemo3 {
    public static void main(String[] args) {
        //创建集合
        TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //o1 -- 待插入的元素
                //o2 -- 已经存在树中的根
                int cha = o2.age - o1.age;
                return (cha == 0) ? o2.name.compareTo(o1.name):cha;
            }
        });
        //新建学生对象
        Student student1 = new Student("小一", 9);
        Student student2 = new Student("小二", 34);
        Student student3 = new Student("小三", 12);
        Student student4 = new Student("小四", 6);
        Student student5 = new Student("小五", 9);
        //添加元素
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        treeSet.add(student5);

        //遍历
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}

7.自然排序和比较器排序的底层源码:

public class TreeSet<E>{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();

    public TreeSet() {
        this(new TreeMap<E,Object>()); // this(..)
    }

    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    // treeSet.add(s2);
    public boolean add(E e) {
        //TreeSet中add方法底层调用的是TreeMap中的put方法
        //e -- s2
        //PRESENT -- new Object()
        return m.put(e, PRESENT)==null;
    }
}


public class TreeMap{
    private final Comparator<? super K> comparator;
    private transient Entry<K,V> root;

    public TreeMap() {
        comparator = null;
    }

    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

    public V put(K key, V value) {
        // key -- "hadoop"
        // value -- new Object()
        Entry<K,V> t = root; // null
        //如果说是第一个元素插入进来,这个元素就作为红黑树的根保存
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null); // s1
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; // null
        //比较器排序
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //自然排序
        else {
            if (key == null) // s2
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t; // "java"
                cmp = k.compareTo(t.key); // 1
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }



}

8.TreeSet中的排序总结:

1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法, 但是要保证元素的类实现Comparable接口,重写compareTo方法。

2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法, 传入一个实现了Comparator接口的子类对象,重写compare方法。

七.Collection集合继承体系总结:

​​​​​​​

​​​​​​​

 

八.Map接口及其实现子类HasMap

1.概述:

(1)Map集合中的元素是一个键值对

(2)一个键对应一个值,键不允许重复,键是唯一的

(3)值可以发生重复

2.Map接口成员方法(一)

V remove(Object key)                  根据键删除整个键值对,因为键是唯一的,返回键对应的值

V remove(Object key)                              判断集合中是否包含键

boolean containsKey(Object key)                 判断集合中是否包含键

boolean containsValue(Object value)           判断集合中是否包含值

boolean isEmpty()                                        判断集合是否为空

void clear()                                                   清空集合

int size()                                                       获取元素的个数,键值对的个数

演示:


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

/*
    Map集合继承体系:
        1、Map集合中的元素是一个键值对
        2、一个键对应一个值,键不允许重复,键是唯一的
        3、值可以发生重复
    子类:HashMap<K,V>
    成员方法:
        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 MapDemo1 {
    public static void main(String[] args) {
        //创建HashMap的对象
        //HashMap()
        //构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
        HashMap<String, Integer> map1 = new HashMap<>();
        //V put(K key,V value) 向集合中添加一个元素键值对,如果键已经存在集合中,值会进行覆盖处理,返回被覆盖的值
        System.out.println(map1.put("小王", 19));    //null
        System.out.println("++++++++++++++++++++++++++++++++++++++++++");
        System.out.println(map1.put("小王", 33));      //19
        System.out.println(map1);         //{小王=33}
        map1.put("小李", 18);
        map1.put("小帅", 13);
        map1.put("小七", 12);
        //V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
        System.out.println(map1.remove("小七"));      //12
        System.out.println(map1);     //{小李=18, 小王=33, 小帅=13}

        //boolean containsKey(Object key)  //判断集合中是否包含键
        System.out.println(map1.containsKey("小"));     //false

        //boolean containsValue(Object value)   //判断集合中是否包含值
        map1.put("小九",12);
        System.out.println(map1.containsValue(12));  //true

        //void clear()          //清空集合
//        map1.clear();
//        System.out.println(map1);     //{}

        //boolean isEmpty()        //判断集合是否为空
        System.out.println(map1.isEmpty());

        //int size() 获取元素的个数,键值对的个数
        System.out.println(map1.size());

    }
}

3.与遍历有关的成员方法(二)

Set<K> keySet()              获取所有的键组成一个Set集合返回

Set<K> keySet()              获取所有的键组成一个Set集合返回

Collection<V> values()     获取所有的值组成一个Set集合返回

Set<Map.Entry<K,V>> entrySet()              获取所有的键值对组成一个Set集合返回

演示:


import java.util.Collection;
import java.util.HashMap;
import java.util.TreeSet;

/*
成员方法:
    V get(Object key)
    Set<K> keySet()
    Collection<V> values()
    Set<Map.Entry<K,V>> entrySet()


    Map集合遍历的方式:
        1、先获取所有的键,遍历键获取对应的值
        2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
 */
public class MapDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1001,"曹操");
        map.put(1002,"曹丕");
        map.put(1003,"曹纯");
        map.put(1004,"曹叡");
        map.put(1005,"曹冲");
        //V get(Object key) 根据键获取值
        System.out.println(map.get(1001)); //曹操

        //Set<K> keySet() 获取所有的键组成一个Set集合返回
        System.out.println(map.keySet());        //[1001, 1002, 1003, 1004, 1005]

        //Collection<V> values() 获取所有的值组成一个Set集合返回
        Collection<String> values = map.values();
        System.out.println(values);             //[曹操, 曹丕, 曹纯, 曹叡, 曹冲]

        // Set<Map.Entry<K,V>> entrySet()  获取所有的键值对组成一个Set集合返回
        System.out.println(map.entrySet());     //[1001=曹操, 1002=曹丕, 1003=曹纯, 1004=曹叡, 1005=曹冲]

    }
}

 4.Map集合遍历的方式:

(1)先获取所有的键,遍历键获取对应的值

(2)直接获取所有的键值对,遍历每一个键值对,再获得每一个键和值

演示:

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

/*
        Map集合遍历的方式:
        1、先获取所有的键,遍历键获取对应的值
        2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
 */
public class MapDemo3 {
    public static void main(String[] args){
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1001,"曹操");
        map.put(1002,"曹丕");
        map.put(1003,"曹纯");
        map.put(1004,"曹叡");
        map.put(1005,"曹冲");
        //方式一:先获取所有的键,遍历键获取对应的值
        Set<Integer> keys = map.keySet();
        for (Integer key : keys) {
            System.out.println(key+"=="+map.get(key));
        }
        System.out.println("---------------------------------------------");
        //方式二:先获取所有的键值对,遍历键值对获取键和值
        for (Map.Entry<Integer, String> keyValue : map.entrySet()) {
            System.out.println(keyValue.getKey()+"=="+keyValue.getValue());
        }


    }
}

 九.TreeMap类

1.概述:

(1)TreeMap: 底层数据结构是红黑树

(2)根据创建时调用的构造方法不同,map中的键排序的规则不同

(3)创建TreeMap是无参构造方法的话,将来map中的键是以自然排序

(4)创建TreeMap是有参构造方法,传入Comparator<>接口的实现类对象(匿名内部类的方式)

演示:

import java.util.Comparator;
import java.util.TreeMap;

/*
    TreeMap: 底层数据结构是红黑树
    根据创建时调用的构造方法不同,map中的键排序的规则不同
    创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
    创建TreeMap是有参构造方法,传入Comparator接口的实现类对象(匿名内部类的方式)
 */
public class TreeMapDemo1 {
    public static void main(String[] args) {
//        TreeMap<Integer, String> treeMap = new TreeMap<>();          //自然排序
        TreeMap<Integer, String> treeMap = new TreeMap<>(new Comparator<Integer>() {          //比较器排序
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        treeMap.put(1004, "小名");
        treeMap.put(1005, "小花");
        treeMap.put(1003, "小王");
        treeMap.put(1002, "小李");
        treeMap.put(1001, "小帅");
        System.out.println(treeMap);     //{1001=小帅, 1002=小李, 1003=小王, 1004=小名, 1005=小花}
    }
}

注意:所有的包装类都实现了Comparator<>接口,但是我们自己自定义的类必须要手动添加Comparator<>接口。

2.Map集合练习

(1)"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)


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

/*
 "aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
 */
public class TreeMapTest1 {
    public static void main(String[] args) {
        TreeMap<Character, Integer> treeMap = new TreeMap<>();
        String s = "aababcabcdabcde";
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (!treeMap.containsKey(c)) {
                treeMap.put(c, 1);
            } else {
                treeMap.put(c, treeMap.get(c) + 1);
            }
        }

        //遍历
        Set<Character> keys = treeMap.keySet();
        for (Character key : keys) {
//            System.out.print(key+"("+treeMap.get(key)+")");         //a(5)b(4)c(3)d(2)e(1)
        }

    }
}

(2)完成对HashMap嵌套ArrayList的遍历

import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
    HashMap嵌套ArrayList
    每一个人都有一些爱好
    HashMap<Person,ArrayList<String>>
 */
class Person {
    String name;
    int age;

    public Person() {
    }

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

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

public class TreeMapTest2 {
    public static void main(String[] args) {
        //创建学生对象
        Person person1 = new Person("小明", 23);
        Person person2 = new Person("小李", 33);
        Person person3 = new Person("小王", 13);

        //创建ArrayList集合对象,并添加爱好
        ArrayList<String> arrayList1 = new ArrayList<>();
        arrayList1.add("打羽毛球");
        arrayList1.add("打蓝球");
        ArrayList<String> arrayList2 = new ArrayList<>();
        arrayList2.add("养生");
        arrayList2.add("旅游");
        ArrayList<String> arrayList3 = new ArrayList<>();
        arrayList3.add("吃好吃的");
        arrayList3.add("和小伙伴玩");


        HashMap<Person, ArrayList> hashMap = new HashMap<>();
        hashMap.put(person1, arrayList1);
        hashMap.put(person2, arrayList2);
        hashMap.put(person3, arrayList3);

        //遍历
        Set<Map.Entry<Person, ArrayList>> keyValues = hashMap.entrySet();
        for (Map.Entry<Person, ArrayList> keyValue : keyValues) {
            for (Object habby : keyValue.getValue()) {
                System.out.println("姓名和年龄:"+keyValue.getKey() + " "+"爱好:" + habby);
            }
        }
    }
}

 十.Collections类

1.概述:

   针对集合操作的工具类

2.Collections成员方法

(1)public static <T> void sort(List<T> list)                             对集合排序

(2)public static <T> int binarySearch(List<?> list,T key)       二分查找

(3)public static <T> T max(Collection<?> coll)                     获取最大值

(4)public static void reverse(List<?> list)                              使集合中元素逆序

(5)public static void shuffle(List<?> list)                               将集合中的元素随机打乱

                                                                             这里的shuffle和hadoop中的shuffle不是一个概念

注意:Collections工具类中的synchronizedXxx(xxx)方法可以将线程不安全的类变成线程安全的类,转换后使用方式不变

演示:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/*
    Collections工具类可以将线程不安全的类变成线程安全的类,使用方式不变
    synchronizedXxx(xxx)
 */
public class CollectionsDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        List<String> list1 = Collections.synchronizedList(list);
        list1.add("java");
        list1.add("java");
        list1.add("java");
        list1.add("java");
        System.out.println(list1);

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1504988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL使用】show processlist 命令详解

show processlist 命令详解 一、命令含义二、命令返回参数三、Command值解释四、State值解释五、参考资料 一、命令含义 对于一个MySQL连接&#xff0c;或者说一个线程&#xff0c;任何时刻都有一个状态&#xff0c;该状态表示了MySQL当前正在做什么。SHOW PROCESSLIST 命令的…

浏览器与Node.js事件循环:异同点及工作原理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

2024年目标检测研究进展

YOLOv9 图片来源网络 YOLO相关的研究&#xff1a;https://blog.csdn.net/yunxinan/article/details/103431338

【蓝桥杯-单片机】LED和按键小练习:Led彩灯控制系统

文章目录 【蓝桥杯-单片机】LED和按键小练习&#xff1a;Led彩灯控制系统01 题目描述02 题目解答03 本题总结整体逻辑框架&#xff08;详细版&#xff09;整体逻辑框架&#xff08;缩略版&#xff09;按键读取模块按键消抖模块流水灯显示模式&#xff08;1&#xff09;从上向下…

基于Java的高校实验室管理系统(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…

【Python】成功解决ModuleNotFoundError: No module named ‘matplotlib‘

【Python】成功解决ModuleNotFoundError: No module named ‘matplotlib’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448…

Pygame教程07:键盘常量+键盘事件的2种捕捉方式

------------★Pygame系列教程★------------ Pygame教程01&#xff1a;初识pygame游戏模块 Pygame教程02&#xff1a;图片的加载缩放旋转显示操作 Pygame教程03&#xff1a;文本显示字体加载transform方法 Pygame教程04&#xff1a;draw方法绘制矩形、多边形、圆、椭圆、弧…

数学建模-动态规划(美赛运用)

动态规划模型的要素是对问题解决的抽象&#xff0c;其可分为&#xff1a; 阶段。指对问题进行解决的自然划分。例如&#xff1a;在最短线路问题中&#xff0c;每进行走一步的决策就是一个阶段。 状态。指一个阶段开始时的自然状况。例如&#xff1a;在最短线路问题中&#xff…

Linux文件和文件夹操作

前言&#xff1a; 相较于前面背诵的诸多内容&#xff0c;可能现在的部分就需要多多的练习了&#xff0c;难度也慢慢提升。 那就大家一起慢慢努力吧&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 目录 一、Linux目录结构 &#xff08;一&#xff09;Window…

HTML概念

文章目录 1. HTML 概念1.1. 简介1.2. 思想1.3. 特点1.4. 语法1.4.1. 标签1.4.2. 属性1.4.3. 标签体1.4.4. 注释 2. HTML 实体2.1. 练习 3. HTML 结构3.1. <!DOCTYPE html>声明3.2. html根标签 4. 补充4.1. 管理文件4.2. 配置 VsCode4.2. 配置 VsCode 1. HTML 概念 1.1. 简…

【项目笔记】java微服务:黑马头条(day01)

文章目录 环境搭建、SpringCloud微服务(注册发现、服务调用、网关)1)课程对比2)项目概述2.1)能让你收获什么2.2)项目课程大纲2.3)项目概述2.4)项目术语2.5)业务说明 3)技术栈4)nacos环境搭建4.1)虚拟机镜像准备4.2)nacos安装 5)初始工程搭建5.1)环境准备5.2)主体结构 6)登录6.1…

微信小程序-侧滑删除

简介 movable-view和movable-area是可移动的视图容器&#xff0c;在页面中可以拖拽滑动。 本篇文章将会通过该容器实现一个常用的拖拽按钮功能。 使用效果 代码实现 side-view.wtml 布局见下面代码&#xff0c;left view为内容区域&#xff0c;right view为操作按钮&a…

因为一次 Kafka 宕机,终于搞透了 Kafka 高可用原理!

Kafka宕机引发的高可用问题 问题要从一次Kafka的宕机开始说起。 但最近系统测试人员常反馈偶有Kafka消费者收不到消息的情况&#xff0c;登陆管理界面发现三个节点中有一个节点宕机挂掉了。但是按照高可用的理念&#xff0c;三个节点还有两个节点可用怎么就引起了整个集群的消…

排序算法的对比

类别排序方法时间复杂度空间复杂度稳定性平均情况特殊情况 插入 排序 插入排序基本有序最优稳定希尔排序不稳定 选择 排序 选择排序不稳定堆排序不稳定 交换 排序 冒泡排序稳定快速排序基本有序最差不稳定归并排序稳定基数排序稳定

Redis实现用户活跃排行榜

在这里用户活跃度排行榜&#xff0c;主要是基于redis的zset数据结构来实现的&#xff0c;下面来看一下实例。 方案设计 来看一下业务场景先 1.场景说明 在技术派中&#xff0c;提供了一个用户的活跃排行榜&#xff0c;当然作为一个博客社区&#xff0c;更应该实现的是作者排…

惬意上手MySQL

大家好&#xff0c;我又来写博客了&#xff0c;今天给大家介绍一下MySQL,如果你只想让MySQL作为自己的辅助开发工具&#xff0c;那这一篇文章就够了&#xff0c;如果想作为一门语言来学习&#xff0c;那你可以看此文章了解一些基础。 MySQL介绍 数据库可分为关系型数据库和非关…

《C语言都有哪些字符串处理函数?》

目录 17个字符串处理函数 1. gets()--读 2.fgets()--从指定文件内读 3.puts()--输出 4.fputs()--写入到指定文件中 5.strlen()--计算字符串长度 6.strcpy()--复制 7.strncpy()--复制前n个字符 8.strcat()--字符串连接 9.strncat()--将前n个字符连接 10.strcmp()--比…

Paimon 与 Spark 的集成(二):查询优化

Paimon Apache Paimon (incubating) 是一项流式数据湖存储技术&#xff0c;可以为用户提供高吞吐、低延迟的数据摄入、流式订阅以及实时查询能力。Paimon 采用开放的数据格式和技术理念&#xff0c;可以与 Flink / Spark / Trino 等诸多业界主流计算引擎进行对接&#xff0c;共…

视频远程监控平台EasyCVR集成后播放只有一帧画面的原因排查与解决

智慧安防视频监控平台EasyCVR能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理&#xff0c;平台可支持的接入协议包括&#xff1a;国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议…

增量式编码器与绝对值编码器基础详解

文章目录 1 使用什么样的电信号来表示旋转和角度信息?1.1 表示相对角度的增量法1.2 表示绝对角度的绝对方法1.3 用脉冲信号表示绝对角度的伪绝对法2 相对角和绝对角的优缺点3 总结1 使用什么样的电信号来表示旋转和角度信息? 在第二部分中,我们解释了旋转和角度信息大致分为…