JAVA-异常、Collection

目录

1.异常

1.1 认识异常

1.2 自定义异常

1.3 异常的处理

 2.集合体系架构

2.1 Collection集合体系

         2.2 Collection的方法

2.3 Collection的遍历方式

2.3.1 迭代器

2.3.2 增强for

2.3.3 lambda表达式

2.3.4 遍历对象

 2.3.5 集合的并发修改异常

2.4 List集合

2.4.1 特点、特有方法

2.4.2 遍历方式

2.4.3 ArrayList集合的底层原理

2.4.4 LinkedList集合的底层原理

2.4.5 手写链表

2.5 set集合

2.5.1 特点

2.5.2 HashSet集合的底层原理

深入理解HashSet集合去重复机制

2.5.3 LinkedHashSet集合的底层原理

2.5.4 TreeSet集合


1.异常

1.1 认识异常

package com.itheima.d1_exception;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo1 {
    public static void main(String[] args) throws ParseException {
        // 目标:认识异常。
        System.out.println("===开始===");
        int[] arr = {11, 22, 33};
        System.out.println(arr[0]);
        System.out.println(arr[1]);
        System.out.println(arr[2]);
        // System.out.println(arr[3]); // ArrayIndexOutOfBoundsException 数组索引越界异常

        String name = null;
        // System.out.println(name.length()); // NullPointerException 空指针异常

        // System.out.println(10 / 0); // ArithmeticException 数学操作异常

        Object o = "张麻子";
        //编译阶段不会报错,运行报错类型转换异常
        // Integer i = (Integer) o; // ClassCastException 类型转换异常

        String s = "23a";
        int it = Integer.valueOf(s); // NumberFormatException 数字转换化异常
        System.out.println(it);

        parseDate("2024-03-19 13:20:31");

        System.out.println("===结束===");
    }
    //抛出异常
    public static void parseDate(String s) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s); // 编译时异常,写代码时就标红报错!
        System.out.println(d);
    }
}

1.2 自定义异常

 我们可以用try{}catch{}来捕获异常。

package com.itheima.d1_exception;

public class ExceptionDemo2 {
    public static void main(String[] args) {
        //目标:搞清楚异常的作用。
        System.out.println("开始...");
        try {
            System.out.println(divide(10, 2));
            System.out.println("成功了!");
        } catch (Exception e) {
            System.out.println("失败了!");
            //捕获异常,并打印出异常信息
            e.printStackTrace();//打出这个异常信息
        }
        System.out.println("结束...");
    }

    public static int divide(int a, int b){

        if (b == 0){
            System.out.println("参数有问题~~~");
            //return -1;//返回-1有可能除的结果就是-1与真正结果重复
            //抛出一个异常作为返回值,通知上层,这里出现了bug
            throw new RuntimeException("/ by 0! ");
        }

        int c = a / b;
        return c;
    }
}

 自定义运行时异常:

package com.itheima.d1_exception;

public class ExceptionDemo3 {
    public static void main(String[] args) {
        // 目标:自定义异常。
        System.out.println("开始....");
        try {
            save(50);
            System.out.println("执行成功了");
        } catch (Exception e) {
            e.printStackTrace();//打印异常信息
            System.out.println("执行失败了");
        }
        System.out.println("结束....");
    }

    public static void save(int age){
        if (age <= 0 || age > 150){
            //这个年龄非法!创建异常对象并立即抛出去
            //不能用输出语句,如果用输出语句项目上线并没有控制台,看不到异常信息
            throw new ItheimaAgeIllegalRuntimeException("/age is xiagao!");
        }
        System.out.println("年龄保存成功了,年龄是:" + age);
    }
}
package com.itheima.d1_exception;
// 自定义运行时异常

/**
 1、继承 RuntimeException
 2、重写构造器
 */
public class ItheimaAgeIllegalRuntimeException extends RuntimeException{
    public ItheimaAgeIllegalRuntimeException() {
    }

    public ItheimaAgeIllegalRuntimeException(String message) {
        super(message);
    }
}

 自定义编译时异常:

package com.itheima.d1_exception;

public class ExceptionDemo4 {
    public static void main(String[] args) {
        // 目标:自定义异常2。
        System.out.println("开始。。。。");
        try {
            save(250);
            System.out.println("执行成功了");
        } catch (Exception e) {
            e.printStackTrace(); // 打印异常对象信息
            System.out.println("执行失败了");

        }
        System.out.println("结束。。。。");
    }

    public static void save(int age) throws ItheimaAgeIllegalException {
        // throw 方法内部使用的,创建异常并从此点抛出去。
        // throws 方法上,抛出方法内部的异常给调用者。
        if(age <= 0 || age > 150){
            // 这个年龄非法!创建异常对象并立即抛出去
            throw new ItheimaAgeIllegalException("/age is xiagao!");
        }
        System.out.println("年龄保存成功了,年龄是:" + age);
    }
}
package com.itheima.d1_exception;
// 自定义编译时异常

/**
 1、继承 Exception
 2、重写构造器
 */
public class ItheimaAgeIllegalException extends Exception{
    public ItheimaAgeIllegalException() {
    }

    public ItheimaAgeIllegalException(String message) {
        super(message);
    }
}

 我们应该尽量定义运行时异常,编译时异常的耦合性问题如:底层方法的异常(如数据库连接异常)会逐层向上传播,迫使上层业务代码处理与业务无关的技术异常,对程序员干扰强烈。

1.3 异常的处理

 底层异常往外抛,最外层集中捕获,代码能写过去即可。

package com.itheima.d1_exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo5 {
    public static void main(String[] args)  {
        // 目标:异常的处理机制
        System.out.println("===开始===");
        try {
            parseDate("2023-11-11 11:11:11");
            System.out.println("成功了");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("失败了!");
        }
        System.out.println("===结束===");
    }

    public static void parseDate(String s) throws ParseException, FileNotFoundException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s);//编译时异常,写代码时就报错!
        System.out.println(d);

        InputStream is = new FileInputStream("D:/meinv.png");
    }
}
package com.itheima.d1_exception;

import java.util.Scanner;

public class ExceptionDemo6 {
    public static void main(String[] args) {
        // 目标:异常的处理方式2:捕获异常,尝试修复。
        while (true) {
            try {
                double price = getPrice();
                System.out.println("本商品定价是:" + price);
                break;
            } catch (Exception e) {
                System.out.println("您输入的价格是瞎搞的!");
            }
        }
    }

    public static double getPrice(){
        Scanner sc = new Scanner(System.in);
        System.out.println("请您输入一个合法的价格:");
        double price = sc.nextDouble();
        return price;
    }
}

 2.集合体系架构

 2.1 Collection集合体系

 2.2 Collection的方法

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

public class CollectionAPTDemo1 {
    public static void main(String[] args) {
        // 目标:掌握Collection提供的常用方法:是全部单列集合都可以直接用的。
        Collection<String> list = new ArrayList<>(); // 多态
        // 1、添加数据 boolean add(E e)
        list.add("java1");
        list.add("java1");
        list.add("赵敏");
        list.add("赵敏");
        list.add("小昭");
        list.add("灭绝师太");
        System.out.println(list); // [java1, java1, 赵敏, 赵敏, 小昭, 灭绝师太]

        // 2、清空集合
        //list.clear();
        //System.out.println(list); // []

        // 3、判断集合是否为空
        System.out.println(list.isEmpty());

        // 4、直接删除集合中的某个数据:默认只能删除第一个java1
        System.out.println(list.remove("java1"));
        System.out.println(list);


        // 5、判断集合中是否包含某个数据
        System.out.println(list.contains("java1")); // true
        System.out.println(list.contains("Java1")); // false

        // 6、获取集合的大小(元素个数)
        System.out.println(list.size());

        // 7、把集合转化成数组。
        Object[] array = list.toArray();
        System.out.println(Arrays.toString(array));

        // 拓展
        String[] arrays = list.toArray(String[]::new);
        System.out.println(Arrays.toString(arrays));

        // 8、拓展一下:把别人集合的数据加给自己
        Collection<String> c1 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");

        Collection<String> c2 = new ArrayList<>();
        c2.add("java2");
        c2.add("java3");
        // 把c2集合的数据全部倒入给c1集合
        c1.addAll(c2);

        System.out.println(c1);
        System.out.println(c2);

    }
}

2.3 Collection的遍历方式

2.3.1 迭代器

package com.itheima.d4_collection_travesal;

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

public class CollectionDemo1 {
    public static void main(String[] args) {
        // 目标:掌握Collection集合的遍历方式一:迭代器遍历。
        // 1、准备一个集合
        ArrayList<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("古力娜扎");
        list.add("马尔扎哈");
        System.out.println(list);
        // list = [赵敏, 古力娜扎, 马尔扎哈]
        //          it
        // 2、得到这个集合对象的迭代器对象。
        Iterator<String> it = list.iterator();
        //先把第一个取完,然后把指针移动到下一个
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        // System.out.println(it.next());

        // 3、使用循环改进
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
    }
}

2.3.2 增强for

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

public class CollectionDemo2 {
    public static void main(String[] args) {
        // 目标:掌握Collection集合的遍历方式二:增强for(foreach遍历)。
        // 1、准备一个集合
        Collection<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("古力娜扎");
        list.add("马尔扎哈");
        list.add("卡扎菲");

        // 2、增强for循环遍历集合
        for (String s : list) {
            System.out.println(s);
        }

        // 3、注意:增强for也可以遍历数组。
        int[] ages = {19,18,34,25};
        for (int age : ages) {
            System.out.println(age);
        }

    }
}

 注意:

增强for循环的本质:它是Java提供的一种语法糖,在编译之后会被转化成传统的for循环或者迭代器,在遍历数组时,他会转化成普通的for循环;而遍历集合时,则会使用迭代器。

增强for循环无法修改元素值:在增强for循环里,element是集合或数组中元素的一个副本,并非元素本身。所以,修改element的值,仅仅是改变了这个临时副本,并不会对原始集合或数组里的元素造成影响。        

2.3.3 lambda表达式

package com.itheima.d4_collection_travesal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class CollectionDemo3 {
    public static void main(String[] args) {
        // 目标:掌握Collection集合的遍历方式三:lambda表达式
        // 1、准备一个集合
        ArrayList<String> list = new ArrayList<>();
        list.add("赵敏");
        list.add("古力娜扎");
        list.add("马尔扎哈");
        list.add("卡扎菲");

        //list.forEach()方法接受一个Consumer<String>函数式接口作为参数
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        list.forEach(s -> System.out.println(s));

        list.forEach(System.out::println);



    }
}

2.3.4 遍历对象

package com.itheima.d4_collection_travesal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class Test4 {
    public static void main(String[] args) {
        // 1、创建集合
        Collection<Movie> movies = new ArrayList<>();

        // 2、存入电影对象
        movies.add(new Movie("《消失的她》", "文咏珊,倪妮", 9.5));
        movies.add(new Movie("《八角笼中》", "王宝强", 7.5));
        movies.add(new Movie("《三万里》", "李白", 8.5));

        // 3、遍历集合中的每个电影对象。
        for (Movie m : movies) {
            System.out.println(m.getName() + " " + m.getActor() + " " + m.getScore());
        }
    }
}

 2.3.5 集合的并发修改异常

package com.itheima.d4_collection_travesal;

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

public class CollectionTest5 {
    public static void main(String[] args) {
        // 目标:三种遍历可能出现的并发修改异常问题。
        ArrayList<String> list = new ArrayList<>();
        list.add("Java入门");
        list.add("宁夏枸杞");
        list.add("黑枸杞");
        list.add("人字拖");
        list.add("特技枸杞");
        list.add("枸杞子");

        // 1、使用迭代器遍历集合并删除枸杞:
        // 注意1:如果使用迭代器遍历,并用集合删除数据,会出现并发修改异常,程序出现bug。
        // 注意2: 必须调用迭代器自己的删除方法,才不会出现bug
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String name = it.next();
            if(name.contains("枸杞")){
                //list.remove(name);//如果使用迭代器遍历,并用集合删除数据,会出现并发修改异常,程序出现bug
                it.remove();//必须调用迭代器自己的删除方法,才不会出现bug
                //相当于在内部做了一步i--
            }
        }
        System.out.println(list);

        // 2、使用增强for遍历集合并删除枸杞:(本质就是迭代器) 一定会出错,而且无法解决。
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("Java入门");
        list2.add("宁夏枸杞");
        list2.add("黑枸杞");
        list2.add("人字拖");
        list2.add("特技枸杞");
        list2.add("枸杞子");
//        for (String name : list2) {
//            if(name.contains("枸杞")){
//                list2.remove(name);
//            }
//        }
//        System.out.println(list2);


        // 3、Lambda遍历集合并删除:一定会出错,而且无法解决。
        ArrayList<String> list3 = new ArrayList<>();
        list3.add("Java入门");
        list3.add("宁夏枸杞");
        list3.add("黑枸杞");
        list3.add("人字拖");
        list3.add("特技枸杞");
        list3.add("枸杞子");
        list3.forEach(name -> {
            if(name.contains("枸杞")){
                list3.remove(name);
            }
        });

        // 注意:如果是Arraylist带索引的集合,我们也可以使用for循环删除每次退一步,或者从后面倒着遍历并删除!
    }
}

如果要遍历的同时删除元素,我们可以使用迭代器自己的remove删除方法,才不会出现bug。

如果只是遍历元素,我们可以使用增强for循环和foreach方法

2.4 List集合

2.4.1 特点、特有方法

package com.itheima.d5_list;

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

public class ListDemo1 {
    public static void main(String[] args) {
        // 目标:掌握List的特有方法。
        // 1、创建一个List集合对象:(一行经典代码)
        List<String> list = new ArrayList<>();//多态
        list.add("张无忌");
        list.add("周芷若");
        list.add("小昭");
        list.add("殷素素");
        System.out.println(list); // [张无忌, 周芷若, 小昭, 殷素素]

        // 2、给某个位置插入一个数据
        list.add(1,"学Java的bb");
        System.out.println(list);

        // 3、根据索引删除数据。
        System.out.println(list.remove(2));
        System.out.println(list);

        // 4、修改索引位置处的数据
        list.set(2,"灭绝");
        System.out.println(list);

        // 5、根据索引取数据
        System.out.println(list.get(2));
    }
}

2.4.2 遍历方式

List集合支持的遍历方式:

  • for循环(因为List集合有索引)
  • 迭代器
  • 增强for循环
  • Lambda表达式
package com.itheima.d5_list;

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

public class ListDemo2 {
    public static void main(String[] args) {
        // 目标:掌握List的遍历方式
        // 1、创建一个List集合对象(一行经典代码)
        List<String> list = new ArrayList<>();
        list.add("金毛狮王");
        list.add("谢逊");
        list.add("白眉鹰王");
        System.out.println(list);

        // 一、for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        // 二、迭代器
        //while一次里面只能取一次it.next()
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String name = it.next();
            System.out.println(name);
        }

        // 三、增强for循环
        for (String name : list) {
            System.out.println(name);
        }

        // 四、Lambda
        list.forEach(s -> System.out.println(s));
        //用方法引用来简化
        list.forEach(System.out::println);
    }
}

2.4.3 ArrayList集合的底层原理

ArrayList和LinkedList都是有序,可重复,有索引的,但是他们底层采用的数据结构不同,应用场景不同

 删除和添加效率低只是相对而言的,实际上ArrayList我们还是很常用的

计算机的索引从0开始对于底层寻址的速度比较快

 

 ArrayList的底层是一个动态扩容数组,其核心属性包括:

  • elementData:存储元素的数组
  • size:当前实际存储的元素数量(非数组长度),记录当前集合的大小,同时也是下一个元素的位置

 ArrayList集合适合的应用场景:

  • ArrayList适合:根据索引查询数据,比如根据随机索引取数据(高效),或者数据量不是很大时
  • ArrayList不适合:数据量大的同时,又要频繁的进行增删操作!

2.4.4 LinkedList集合的底层原理

  • 基于双链表实现的

 

 LinkedList可以用来设计队列:先进先出,后进后出,只在链表的首尾进行操作

import java.util.LinkedList;

public class ListTest4 {
    public static void main(String[] args) {
        // 目标:掌握LinkedList的应用
        // 1、做队列
        LinkedList<String> queue = new LinkedList<>();
        //入队
        queue.addLast("第1个人");
        queue.addLast("第2个人");
        queue.addLast("第3个人");
        queue.addLast("第4个人");
        System.out.println(queue);

        //出队
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);
    }
}

 

//2.做栈
        LinkedList<String> stack = new LinkedList<>();
        stack.addFirst("第1颗子弹");
        stack.addFirst("第2颗子弹");
        stack.addFirst("第3颗子弹");
        stack.addFirst("第4颗子弹");
        stack.addFirst("第5颗子弹");
        System.out.println(stack);
//        [第5颗子弹, 第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]

        //出栈
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack);
//        第5颗子弹
//        第4颗子弹
//        第3颗子弹
//        [第2颗子弹, 第1颗子弹]

 2.4.5 手写链表

package com.itheima.d5_list;

import java.util.StringJoiner;

/**
 *单链表
 */
public class MyLinkedList2<E> {

    private int size = 0;

    MyLinkedList2.Node<E> first; //定义头指针

    //定义一个节点类:用于创建节点对象,封装节点数据和下一个节点对象的地址值
    public static class Node<E> {
        E item;
        Node<E> next; // 下一个对象的地址。

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }

    //添加新节点的方法add()
    public boolean add(E e) {
        //维护单链表
        //第一个节点,或者是后面的节点
        //无论是第一个节点还是后面的节点都需要创建一个节点对象,封装这个数据
        //新节点的下一个节点的地址值都为null
        Node<E> newNode = new Node<>(e, null);

        //判断这个节点是否是第一个节点
        if (first == null) {
            first = newNode;
        } else {
            //不是第一个节点,就要找到当前链表的最后一个元素
            //如何找当前链表的最后一个元素,用一个临时变量接收first的值
            //注意:在创建链表的过程中first节点始终不能改变。只能用临时变量接收它进行操作
            Node<E> temp = first;
            while (temp.next != null) {
                temp = temp.next;
            }
            temp.next = newNode;
        }
        size++;
        return true;
    }

    @Override
    public String toString() {
        //用StringJoiner拼接字符串时更高效
        StringJoiner sb = new StringJoiner(", ", "[", "]");
        Node<E> temp = first;
        while (temp != null) {
            //+ "" 是为了保证拼接的内容是字符串
            sb.add(temp.item + "");
            temp = temp.next;
        }
        return sb.toString();
    }

    public int size() {
        return size;
    }

}

class Test1{
    public static void main(String[] args) {
        MyLinkedList<String> list = new MyLinkedList<>();
        list.add("java1");
        list.add("java2");
        list.add("java3");
        list.add("java4");
        list.add("java5");
        list.add("java6");
        list.add("java7");
        list.add("java8");
        list.add("java9");
        list.add("java10");
        list.add("java11");
        System.out.println(list);
    }
}

2.5 set集合

2.5.1 特点

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

public class SetDemo1 {
    public static void main(String[] args) {
        // 目标:了解Set家族的特点:无序,不重复,无索引。
        //Set<String> set = new HashSet<>(); // 多态,一行经典代码。
        Set<String> set = new LinkedHashSet<>(); // 有序,不重复,无索引。
        set.add("张无忌");
        set.add("张无忌");
        set.add("朱九真");
        set.add("周芷若");
        set.add("周芷若");
        set.add("赵敏");
        set.add("小昭");
        System.out.println(set); // [小昭, 周芷若, 赵敏, 张无忌, 朱九真]


    }
}

2.5.2 HashSet集合的底层原理

package com.itheima.d6_set;

public class SetDemo2 {
    public static void main(String[] args) {
        // 目标:拿到对象的哈希值。
        String name1 = "abc";
        String name2 = "acB";

        // 备注:不同对象的哈希值大概率不相同,但可能存在相同的情况。
        System.out.println(name1.hashCode());//96354
        System.out.println(name1.hashCode());//96354
        System.out.println(name2.hashCode());//96352
    }
}

哈希值其实就是一个随机数,不同对象的哈希值有可能会相同 

 HashSet集合的底层原理

  • 基于哈希表实现
  • 哈希表是一种增删查改数据,性能都较好的数据结构

哈希表

  • JDK8之前,哈希表 = 数组 + 链表
  • JDK8开始,哈希表 = 数组 + 链表 + 红黑树

 

用对象的哈希值对底层数组的长度求余

哈希表是一种增删查改数据性能都较好的结构,牺牲内存,只适合无序,不重复,无索引的业务。

 默认加载因子:

  • 在Java里大多数哈希表的默认加载因子是0.75(也存在特殊情况比如 IdentityHashMap 的默认加载因子为 0.666,而 EnumMap 则不使用加载因子)当哈希表中的条目数量超出了加载因子与当前容量的乘积时,哈希表就会进行扩容操作(即执行 resize)。

在上面的例子中就是0.75  * 16 = 12,当存储的元素超过12个之后,哈希表就会自动扩容为 32。

  • 扩容操作会让哈希表的容量翻倍(即扩容为原来的两倍)同时重新计算所有键的哈希位置,这一过程也被称作 rehashing。
  • 为什么默认值是 0.75

    • 这一数值是时间和空间成本之间权衡的结果。
    • 若加载因子较大,像设为 1,那么哈希表对空间的利用率会更高,不过哈希冲突发生的概率也会随之增加,这会导致查询性能下降。
    • 若加载因子较小,例如 0.5,哈希冲突会减少,从而使查询速度更快,但这样会占用更多的空间,造成空间浪费。

 

 

 使用红黑树的好处:

  • 链表的问题:当发生哈希冲突时,传统的链表结构(每个槽位存储一个链表)在最坏情况下的查找时间复杂度为 O(n)(所有元素都在同一个链表中)。
  • 红黑树的优势:当一个槽位中的元素数量超过阈值(默认为 8)且哈希表容量≥64 时,链表会转换为红黑树,此时查找、插入和删除操作的时间复杂度优化为 O(log n),避免了链表过长导致的性能退化。红黑树可以将元素铺开,性能更好,基于二分查找的算法。
  • 自适应调整结构:当元素数量减少(例如删除操作后)且树的节点数少于阈值(默认为 6)时,红黑树会自动转换回链表,平衡空间和时间开销。

 平衡二叉树任意两棵子树的左右高度差不超过1

 任何一条路径下黑节点的个数是一样的

深入理解HashSet集合去重复机制

 重写hashCode()和equals(),当我们向HashSet中添加一个新元素时,HashSet会先调用该元素的hasCode()方法,用对象中的元素来返回哈希值,如果两个对象的内容时一样的,返回的哈希值就是一样的。

equals() 的作用:equals() 方法用于确保当两个对象的内容相同时,它们被视为同一个对象。

有人可能会问,已经有了hashCode()方法,当对象的内容相等时,返回的哈希值是一样的,已经能判断出哈希表中重复的元素,为什么还要重写equals方法?

因为两个对象的哈希值相同,并不一定意味着他们相等,哈希值也会有所重复,这就需要重写equals()方法时,必须同时重写hashCode()方法,要保证两个具有相等哈希值的对象内容也是相同的,才能真正确定时重复的元素,从而保证集合中元素的唯一性。

2.5.3 LinkedHashSet集合的底层原理

2.5.4 TreeSet集合

package com.itheima.d6_set;
// 方法一:类实现Comparable接口,重写compareTo方法
public class Girl implements Comparable<Girl>{
   private String name;
   private char sex;
   private int age;
   private double height;

    public Girl() {
    }

    public Girl(String name, char sex, int age, double height) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                ", height=" + height +
                '}' + '\n';
    }

    @Override
   public int compareTo(Girl o) {
        return o.age - this.age >= 0 ? 1 : -1;
    }
}
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

import static java.lang.Double.*;

public class SetDemo5 {
    public static void main(String[] args) {
        //目标:TreeSet排序对象
        //方式二:TreeSet集合自带比较器对象Comparator
//        Set<Girl> set = new TreeSet<>(new Comparator<Girl>() {
//            @Override
//            public int compare(Girl o1, Girl o2) {
//                return Double.compare(o2.getHeight(),o1.getHeight());
//            }
//        });//排序,不重复,无索引

        //简化代码
        Set<Girl> set = new TreeSet<>((o1, o2) -> Double.compare(o2.getHeight(),o1.getHeight()));


        set.add(new Girl("赵敏",'女',21,169.5));
        set.add(new Girl("刘亦菲",'女',34,167.5));
        set.add(new Girl("李若彤",'女',26,168.5));
        set.add(new Girl("张若男",'女',19,171.5));
        set.add(new Girl("杨幂",'女',34,172.5));

        System.out.println(set);

    }
}

 TreeSet集合对自定义类型的对象排序,有两种方式指定比较规则。

  • 类实现Comoarable接口,重写比较规则
  • 集合自定义Comparator比较器对象,重写比较规则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值