Java基础(2)

发布于 2022-12-30  413 次阅读


java基础(1)被我不小心删了。。。还好留了个备份,但是和现在的冲突了,只能留个图片

http://43.128.46.201:8888/down/kVbDnSFrjPF2.jpeg

包装类

其实就是8种基本数据类型对应的引用类型。

基本数据类型引用数据类型
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

为什么提供包装类?
Java为了实现一切皆对象,为8种基本类型提供了对应的引用类型。
后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型。

自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量。
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量。

包装类的特有功能
包装类的变量的默认值可以是null,容错率更高。
可以把基本类型的数据转换成字符串类型(用处不大)
调用toString()方法得到字符串结果。
调用Integer.toString(基本类型的数据)
可以把字符串类型的数值转换成真实的数据类型(真的很有用)
Integer.parseInt(“字符串类型的整数”)
Double.parseDouble(“字符串类型的小数”)

/**
    目标:明白包装类的概念,并使用。
 */
public class Test {
    public static void main(String[] args) {
        int a = 10;
        Integer a1 = 11;
        Integer a2 = a; // 自动装箱
        System.out.println(a);
        System.out.println(a1);

        Integer it = 100;
        int it1 = it; // 自动拆箱
        System.out.println(it1);

        double db = 99.5;
        Double db2 = db; // 自动装箱了
        double db3 = db2; // 自动拆箱
        System.out.println(db3);

        // int age = null; // 报错了!
        Integer age1 = null;
        Integer age2 = 0;

        System.out.println("-----------------");
        // 1、包装类可以把基本类型的数据转换成字符串形式。(没啥用)
        Integer i3 = 23;
        String rs = i3.toString();
        System.out.println(rs + 1);

        String rs1 = Integer.toString(i3);
        System.out.println(rs1 + 1);

        // 可以直接+字符串得到字符串类型
        String rs2 = i3 + "";
        System.out.println(rs2 + 1);

        System.out.println("-----------------");

        String number = "23";
        //转换成整数
        // int age = Integer.parseInt(number);
        int age = Integer.valueOf(number);
        System.out.println(age + 1);

        String number1 = "99.9";
        //转换成小数
//        double score = Double.parseDouble(number1);
        double score = Double.valueOf(number1);
        System.out.println(score + 0.1);
    }
}

Arrays类

数组操作工具类,专门用于操作数组元素的。

Arrays类的常用API

方法名说明
public static String toString​(类型[] a)返回数组的内容(字符串形式)
public static void sort​(类型[] a)对数组进行默认升序排序
public static <T> void sort​(类型[] a, Comparator<? super T> c)使用比较器对象自定义排序
public static int binarySearch​(int[] a, int key)二分搜索数组中的数据,存在返回索引,不存在返回-1
public class ArraysDemo1 {
    public static void main(String[] args) {
        // 目标:学会使用Arrays类的常用API ,并理解其原理
        int[] arr = {10, 2, 55, 23, 24, 100};
        System.out.println(arr);

        // 1、返回数组内容的 toString(数组)
//        String rs = Arrays.toString(arr);
//        System.out.println(rs);

        System.out.println(Arrays.toString(arr));

        // 2、排序的API(默认自动对数组元素进行升序排序)
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));

        // 3、二分搜索技术(前提数组必须排好序才支持,否则出bug)
        int index = Arrays.binarySearch(arr, 55);
        System.out.println(index);

        // 返回不存在元素的规律: - (应该插入的位置索引 + 1)
        int index2 = Arrays.binarySearch(arr, 22);
        System.out.println(index2);


        // 注意:数组如果么有排好序,可能会找不到存在的元素,从而出现bug!!
        int[] arr2 = {12, 36, 34, 25 , 13,  24,  234, 100};
        System.out.println(Arrays.binarySearch(arr2 , 36));
    }

}

Arrays类的排序方法

方法名说明
public static void sort​(类型[] a)对数组进行默认升序排序
public static <T> void sort​(类型[] a, Comparator<? super T> c)使用比较器对象自定义排序
import java.util.Arrays;
import java.util.Comparator;

public class ArraysDemo2 {
    public static void main(String[] args) {
        // 目标:自定义数组的排序规则:Comparator比较器对象。
        // 1、Arrays的sort方法对于有值特性的数组是默认升序排序
        int[] ages = {34, 12, 42, 23};
        Arrays.sort(ages);
        System.out.println(Arrays.toString(ages));

        // 2、需求:降序排序!(自定义比较器对象,只能支持引用类型的排序!!)
        Integer[] ages1 = {34, 12, 42, 23};
        /**
           参数一:被排序的数组 必须是引用类型的元素
           参数二:匿名内部类对象,代表了一个比较器对象。
         */
        Arrays.sort(ages1, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                // 指定比较规则。
//                if(o1 > o2){
//                    return 1;
//                }else if(o1 < o2){
//                    return -1;
//                }
//                return 0;
                // return o1 - o2; // 默认升序
                return o2 - o1; //  降序
            }
        });
        System.out.println(Arrays.toString(ages1));

        System.out.println("-------------------------");
        Student[] students = new Student[3];
        students[0] = new Student("吴磊",23 , 175.5);
        students[1] = new Student("谢鑫",18 , 185.5);
        students[2] = new Student("王亮",20 , 195.5);
        System.out.println(Arrays.toString(students));

        // Arrays.sort(students);  // 直接运行奔溃
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // 自己指定比较规则
                // return o1.getAge() - o2.getAge(); // 按照年龄升序排序!
                // return o2.getAge() - o1.getAge(); // 按照年龄降序排序!!
                // return Double.compare(o1.getHeight(), o2.getHeight()); // 比较浮点型可以这样写 升序
                return Double.compare(o2.getHeight(), o1.getHeight()); // 比较浮点型可以这样写  降序
            }
        });
        System.out.println(Arrays.toString(students));


    }
}
public class Student {
    private String name;
    private int age;
    private double height;

    public Student() {
    }

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

    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;
    }

    public double getHeight() {
        return height;
    }

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

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

Lambda

Lambda表达式是JDK 8开始后的一种新语法形式。
作用:简化匿名内部类的代码写法。
(匿名内部类被重写方法的形参列表) -> {被重写方法的方法体代码。}
注:-> 是语法形式,无实际含义
什么是函数式接口?首先必须是接口、其次接口中有且仅有一个抽象方法的形式

public class LambdaDemo1 {
    public static void main(String[] args) {
        // 目标:学会使用Lambda的标准格式简化匿名内部类的代码形式
        Animal a = new Animal() {
            @Override
            public void run() {
                System.out.println("乌龟跑的很慢~~~~~");
            }
        };
        a.run();

        // 注意:lambda并不是可以简化所有匿名匿名内部类形式!!
//        Animal a1 = () -> {
//            System.out.println("乌龟跑的很慢~~~~~");
//        };
//        a1.run();
    }
}



abstract class Animal{
    public abstract void run();
}
public class LambdaDemo2 {
    public static void main(String[] args) {
        // 目标:学会使用Lambda的标准格式简化匿名内部类的代码形式
        // 注意:Lambda只能简化接口中只有一个抽象方法的匿名内部类形式(函数式接口)
//        Swimming s1 = new Swimming() {
//            @Override
//            public void swim() {
//                System.out.println("老师游泳贼溜~~~~~");
//            }
//        };

//        Swimming s1 = () -> {
//            System.out.println("老师游泳贼溜~~~~~");
//        };

        Swimming s1 = () -> System.out.println("老师游泳贼溜~~~~~");
        go(s1);

        System.out.println("---------------------");
//        go(new Swimming() {
//            @Override
//            public void swim() {
//                System.out.println("学生游泳很开心~~~");
//            }
//        });

//        go(() ->{
//                System.out.println("学生游泳很开心~~~");
//        });

        go(() -> System.out.println("学生游泳很开心~~~"));


    }

    public static void go(Swimming s){
        System.out.println("开始。。。");
        s.swim();
        System.out.println("结束。。。");
    }
}

@FunctionalInterface // 一旦加上这个注解必须是函数式接口,里面只能有一个抽象方法
interface Swimming{
    void swim();
}
public class LambdaDemo3 {
    public static void main(String[] args) {
        Integer[] ages1 = {34, 12, 42, 23};
        /**
         参数一:被排序的数组 必须是引用类型的元素
         参数二:匿名内部类对象,代表了一个比较器对象。
         */
//        Arrays.sort(ages1, new Comparator<Integer>() {
//            @Override
//            public int compare(Integer o1, Integer o2) {
//                return o2 - o1; //  降序
//            }
//        });

//        Arrays.sort(ages1, (Integer o1, Integer o2) -> {
//                return o2 - o1; //  降序
//        });


//        Arrays.sort(ages1, ( o1,  o2) -> {
//            return o2 - o1; //  降序
//        });

        Arrays.sort(ages1, ( o1,  o2 ) ->  o2 - o1 );

        System.out.println(Arrays.toString(ages1));

        System.out.println("---------------------------");
        JFrame win = new JFrame("登录界面");
        JButton btn = new JButton("我是一个很大的按钮");
//        btn.addActionListener(new ActionListener() {
//            @Override
//            public void actionPerformed(ActionEvent e) {
//                System.out.println("有人点我,点我,点我!!");
//            }
//        });

//        btn.addActionListener((ActionEvent e) -> {
//                System.out.println("有人点我,点我,点我!!");
//        });

//        btn.addActionListener(( e) -> {
//            System.out.println("有人点我,点我,点我!!");
//        });

//        btn.addActionListener( e -> {
//            System.out.println("有人点我,点我,点我!!");
//        });

        btn.addActionListener( e -> System.out.println("有人点我,点我,点我!!") );



        win.add(btn);
        win.setSize(400, 300);
        win.setVisible(true);
    }
}

Lambda表达式的省略写法(进一步在Lambda表达式的基础上继续简化)
·参数类型可以省略不写。
·如果只有一个参数,参数类型可以省略,同时()也可以省略。
·如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
·如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"不写

集合Collection

Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。

Collection集合特点
List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。

泛型
集合都是泛型的形式,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。
error:Collection<int> lists = new ArrayList<>();
如果集合中要存储基本类型的数据怎么办?
// 存储基本类型使用包装类
Collection<integer> lists = new ArrayList<>();
Collection<Double> lists = new ArrayList<>();

Collection集合
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

Collection API:

方法名称说明
public boolean add(E e)把给定的对象添加到当前集合中 
public void clear() 清空集合中所有的元素
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数。
public Object[] toArray()把集合中的元素,存储到数组中

/**
    目标:明确Collection集合体系的特点
 */
public class CollectionDemo1 {
    public static void main(String[] args) {
        // 有序 可重复 有索引
        Collection list = new ArrayList();
        list.add("Java");
        list.add("Java");
        list.add("Mybatis");
        list.add(23);
        list.add(23);
        list.add(false);
        list.add(false);
        System.out.println(list);

        // 无序 不重复  无索引
        Collection list1 = new HashSet();
        list1.add("Java");
        list1.add("Java");
        list1.add("Mybatis");
        list1.add(23);
        list1.add(23);
        list1.add(false);
        list1.add(false);
        System.out.println(list1);

        System.out.println("-----------------------------");
        // Collection<String> list2 = new ArrayList<String>();
        Collection<String> list2 = new ArrayList<>(); // JDK 7开始之后后面类型申明可以不写
        list2.add("Java");
        // list2.add(23);
        list2.add("黑马");

        // 集合和泛型不支持基本数据类型,只能支持引用数据类型
        // Collection<int> list3 = new ArrayList<>();
        Collection<Integer> list3 = new ArrayList<>();
        list3.add(23);
        list3.add(233);
        list3.add(2333);

        Collection<Double> list4 = new ArrayList<>();
        list4.add(23.4);
        list4.add(233.0);
        list4.add(233.3);
    }
}

迭代器遍历概述
遍历就是一个一个的把容器中的元素访问一遍。
迭代器在Java中的代表是Iterator,迭代器是集合的专用的遍历方式。

Collection集合获取迭代器
Iterator iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
Iterator中的常用方法
boolean hasNext() 询问当前位置是否有元素存在,存在返回true ,不存在返回false
E next() 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界

/**
    目标:Collection集合的遍历方式。

    什么是遍历? 为什么开发中要遍历?
        遍历就是一个一个的把容器中的元素访问一遍。
        开发中经常要统计元素的总和,找最值,找出某个数据然后干掉等等业务都需要遍历。

    Collection集合的遍历方式是全部集合都可以直接使用的,所以我们学习它。
    Collection集合的遍历方式有三种:
        (1)迭代器。
        (2)foreach(增强for循环)。
        (3)JDK 1.8开始之后的新技术Lambda表达式(了解)

    a.迭代器遍历集合。
        -- 方法:
             public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的
             boolean hasNext():判断是否有下一个元素,有返回true ,反之。
             E next():获取下一个元素值!
        --流程:
            1.先获取当前集合的迭代器
                Iterator<String> it = lists.iterator();
            2.定义一个while循环,问一次取一次。
              通过it.hasNext()询问是否有下一个元素,有就通过
              it.next()取出下一个元素。
    小结:
        记住代码。
 */
public class CollectionDemo01 {
    public static void main(String[] args) {
        ArrayList<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("素素");
        lists.add("灭绝");
        System.out.println(lists);
        // [赵敏, 小昭, 素素, 灭绝]
        //   it

        // 1、得到当前集合的迭代器对象。
        Iterator<String> it = lists.iterator();
//        String ele = it.next();
//        System.out.println(ele);
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        // System.out.println(it.next()); // NoSuchElementException 出现无此元素异常的错误

        // 2、定义while循环
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
        System.out.println("-----------------------------");

    }
}

foreach/增强for循环

/**
     目标:Collection集合的遍历方式。

     什么是遍历? 为什么开发中要遍历?
     遍历就是一个一个的把容器中的元素访问一遍。
     开发中经常要统计元素的总和,找最值,找出某个数据然后干掉等等业务都需要遍历。

     Collection集合的遍历方式是全部集合都可以直接使用的,所以我们学习它。
     Collection集合的遍历方式有三种:
         (1)迭代器。
         (2)foreach(增强for循环)。
         (3)JDK 1.8开始之后的新技术Lambda表达式。

     b.foreach(增强for循环)遍历集合。
         foreach是一种遍历形式,可以遍历集合或者数组。
         foreach遍历集合实际上是迭代器遍历集合的简化写法。
         foreach遍历的关键是记住格式:
            for(被遍历集合或者数组中元素的类型 变量名称 : 被遍历集合或者数组){

            }
 */
public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("殷素素");
        lists.add("周芷若");
        System.out.println(lists);
        // [赵敏, 小昭, 殷素素, 周芷若]
        //  ele

        for (String ele : lists) {
            System.out.println(ele);
        }

        System.out.println("------------------");
        double[] scores = {100, 99.5 , 59.5};
        for (double score : scores) {
            System.out.println(score);
//            if(score == 59.5){
//                score = 100.0; // 修改无意义,不会影响数组的元素值。
//            }
        }
        System.out.println(Arrays.toString(scores));

    }
}

Lambda表达式遍历集合
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。
Collection结合Lambda遍历的API
default void forEach(Consumer action): 结合lambda遍历集合

/**
     目标:Collection集合的遍历方式。

     什么是遍历? 为什么开发中要遍历?
     遍历就是一个一个的把容器中的元素访问一遍。
     开发中经常要统计元素的总和,找最值,找出某个数据然后干掉等等业务都需要遍历。

     Collection集合的遍历方式是全部集合都可以直接使用的,所以我们学习它。
     Collection集合的遍历方式有三种:
         (1)迭代器。
         (2)foreach(增强for循环)。
         (3)JDK 1.8开始之后的新技术Lambda表达式。
     c.JDK 1.8开始之后的新技术Lambda表达式。
 */
public class CollectionDemo03 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("殷素素");
        lists.add("周芷若");
        System.out.println(lists);
        // [赵敏, 小昭, 殷素素, 周芷若]
        //  s
        lists.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

//        lists.forEach(s -> {
//                System.out.println(s);
//        });

        // lists.forEach(s ->  System.out.println(s) );

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

    }
}

List集合

List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复

List集合特有方法
List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。

方法名称说明
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
/**
      目标:ArrayList集合。

      Collection集合体系的特点:
         Set系列集合: 添加的元素,是无序,不重复,无索引的。
             -- HashSet:添加的元素,是无序,不重复,无索引的。
             -- LinkedHashSet:添加的元素,是有序,不重复,无索引的。
         List系列集合:添加的元素,是有序,可重复,有索引的。
             -- LinkedList: 添加的元素,是有序,可重复,有索引的。
             -- ArrayList: 添加的元素,是有序,可重复,有索引的。
             -- Vector 是线程安全的,速度慢,工作中很少使用。

         1、List集合继承了Collection集合的全部功能,"同时因为List系列集合有索引",
         2、因为List集合多了索引,所以多了很多按照索引操作元素的功能:
         3、ArrayList实现类集合底层基于数组存储数据的,查询快,增删慢!
             - public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
             - public E get(int index):返回集合中指定位置的元素。
             - public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
             - public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回更新前的元素值。
      小结:
            ArrayList集合的底层是基于数组存储数据。查询快,增删慢!(相对的)
 */
public class ListDemo01 {
    public static void main(String[] args) {
        // 1.创建一个ArrayList集合对象:
        // List:有序,可重复,有索引的。
        ArrayList<String> list = new ArrayList<>(); // 一行经典代码!
        list.add("Java");
        list.add("Java");
        list.add("HTML");
        list.add("HTML");
        list.add("MySQL");
        list.add("MySQL");

        // 2.在某个索引位置插入元素。
        list.add(2, "黑马");
        System.out.println(list);

        // 3.根据索引删除元素,返回被删除元素
        System.out.println(list.remove(1));
        System.out.println(list);

        // 4.根据索引获取元素:public E get(int index):返回集合中指定位置的元素。
        System.out.println(list.get(1));

        // 5.修改索引位置处的元素: public E set(int index, E element)
        System.out.println(list.set(1, "传智教育"));
        System.out.println(list);
    }
}

ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢。
LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的。

/**
    拓展:List系列集合的遍历方式有:4种。

    List系列集合多了索引,所以多了一种按照索引遍历集合的for循环。

    List遍历方式:
        (1)for循环。(独有的,因为List有索引)。
        (2)迭代器。
        (3)foreach。
        (4)JDK 1.8新技术。
 */
public class ListDemo02 {
    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("java1");
        lists.add("java2");
        lists.add("java3");

        /** (1)for循环。 */
        System.out.println("-----------------------");

        for (int i = 0; i < lists.size(); i++) {
            String ele = lists.get(i);
            System.out.println(ele);
        }


        /** (2)迭代器。 */
        System.out.println("-----------------------");
        Iterator<String> it = lists.iterator();
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }

        /** (3)foreach */
        System.out.println("-----------------------");
        for (String ele : lists) {
            System.out.println(ele);
        }

        /** (4)JDK 1.8开始之后的Lambda表达式  */
        System.out.println("-----------------------");
        lists.forEach(s -> {
            System.out.println(s);
        });

    }
}
/**
     目标:LinkedList集合。

     Collection集合体系的特点:
         Set系列集合: 添加的元素,是无序,不重复,无索引的。
             -- HashSet:添加的元素,是无序,不重复,无索引的。
             -- LinkedHashSet:添加的元素,是有序,不重复,无索引的。
         List系列集合:添加的元素,是有序,可重复,有索引的。
             -- LinkedList: 添加的元素,是有序,可重复,有索引的。
             -- Vector: 添加的元素,是有序,可重复,有索引的。线程安全(淘汰了)
             -- ArrayList: 添加的元素,是有序,可重复,有索引的。

     LinkedList也是List的实现类:底层是基于双链表的,增删比较快,查询慢!!
     LinkedList是支持双链表,定位前后的元素是非常快的,增删首尾的元素也是最快的
     所以LinkedList除了拥有List集合的全部功能还多了很多操作首尾元素的特殊功能:
         - public void addFirst(E e):将指定元素插入此列表的开头。
         - public void addLast(E e):将指定元素添加到此列表的结尾。
         - public E getFirst():返回此列表的第一个元素。
         - public E getLast():返回此列表的最后一个元素。
         - public E removeFirst():移除并返回此列表的第一个元素。
         - public E removeLast():移除并返回此列表的最后一个元素。
         - public E pop():从此列表所表示的堆栈处弹出一个元素。
         - public void push(E e):将元素推入此列表所表示的堆栈。

    小结:
         LinkedList是支持双链表,定位前后的元素是非常快的,增删首尾的元素也是最快的。
         所以提供了很多操作首尾元素的特殊API可的实以做栈和队列现。

         如果查询多而增删少用ArrayList集合。(用的最多的)
         如果查询少而增删首尾较多用LinkedList集合。
 */
public class ListDemo03 {
    public static void main(String[] args) {
        // LinkedList可以完成队列结构,和栈结构 (双链表)
        // 1、做一个队列:
        LinkedList<String> queue = new LinkedList<>();
        // 入队
        queue.addLast("1号");
        queue.addLast("2号");
        queue.addLast("3号");
        System.out.println(queue);
        // 出队
        // System.out.println(queue.getFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);

        // 2、做一个栈
        LinkedList<String> stack = new LinkedList<>();
        // 入栈 压栈 (push)
        stack.push("第1颗子弹");
        stack.push("第2颗子弹");
        stack.push("第3颗子弹");
        stack.push("第4颗子弹");
        System.out.println(stack);

        // 出栈  弹栈 pop
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack);

    }
}

从集合中的一批元素中找出某些数据并删除,如何操作?是否存在问题呢 ?

哪些遍历存在问题?
迭代器遍历集合且直接用集合删除元素的时候可能出现。
增强for循环遍历集合且直接用集合删除元素的时候可能出现。

哪种遍历且删除元素不出问题
迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
使用for循环遍历并删除元素不会存在这个问题。

/**
    目标:研究集合遍历并删除元素可能出现的:并发修改异常问题。
 */
public class Test {
    public static void main(String[] args) {
        // 1、准备数据
        ArrayList<String> list = new ArrayList<>();
        list.add("黑马");
        list.add("Java");
        list.add("Java");
        list.add("赵敏");
        list.add("赵敏");
        list.add("素素");
        System.out.println(list);
        // [黑马, Java, Java, 赵敏, 赵敏, 素素]
        //        it

        // 需求:删除全部的Java信息。
        // a、迭代器遍历删除
        Iterator<String> it = list.iterator();
//        while (it.hasNext()){
//            String ele = it.next();
//            if("Java".equals(ele)){
//                // 删除Java
//                // list.remove(ele); // 集合删除会出毛病
//                it.remove(); // 删除迭代器所在位置的元素值(没毛病)
//            }
//        }
//        System.out.println(list);

        // b、foreach遍历删除 (会出现问题,这种无法解决的,foreach不能边遍历边删除,会出bug)
//        for (String s : list) {
//            if("Java".equals(s)){
//                list.remove(s);
//            }
//        }

        // c、lambda表达式(会出现问题,这种无法解决的,Lambda遍历不能边遍历边删除,会出bug)
//        list.forEach(s -> {
//            if("Java".equals(s)){
//                list.remove(s);
//            }
//        });

        // d、for循环(边遍历边删除集合没毛病,但是必须从后面开始遍历删除才不会出现漏掉应该删除的元素)
        for (int i = list.size() - 1; i >= 0 ; i--) {
            String ele = list.get(i);
            if("Java".equals(ele)){
                list.remove(ele);
            }
        }
        System.out.println(list);
    }
}

泛型

泛型概述

泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。


/**
    目标:泛型的概述。

    什么是泛型?
        泛型就是一个标签:<数据类型>
        泛型可以在编译阶段约束只能操作某种数据类型。

    注意:
        JDK 1.7开始之后后面的泛型申明可以省略不写
    小结:
         泛型就是一个标签。
         泛型可以在编译阶段约束只能操作某种数据类型。
         泛型只能支持引用数据类型。
 */
public class GenericityDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Java2");
        // list.add(23);

        List<String> list1 = new ArrayList();
        list1.add("Java");
//        list1.add(23.3);
//        list1.add(false);
        list1.add("Spring");

//        for (Object o : list1) {
//            String ele = (String) o;
//            System.out.println(ele);
//        }

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

        System.out.println("---------------------");
        // 存储任意类型的元素
        List<Object> list2 = new ArrayList<>();
        list2.add(23);
        list2.add(23.3);
        list2.add("Java");

        // List<int> list3 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>();
    }
}

泛型可以在很多地方进行定义:
类后面->泛型类
方法申明上->泛型方法
接口后面->型接口

自定义泛型

定义类时同时定义了泛型的类就是泛型类。
泛型类的格式:修饰符 class 类名<泛型变量>{ }
范例:public class MyArrayList { }
此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等。
作用:编译阶段可以指定数据类型,类似于集合的作用。

import java.util.ArrayList;

public class MyArrayList<E> {
    private ArrayList lists = new ArrayList();

    public void add(E e){
        lists.add(e);
    }

    public void remove(E e){
        lists.remove(e);
    }

    @Override
    public String toString() {
        return lists.toString();
    }
}
public class Test {
    public static void main(String[] args) {
        // 需求:模拟ArrayList定义一个MyArrayList ,关注泛型设计
        MyArrayList<String> list = new MyArrayList<>();
        list.add("Java");
        list.add("Java");
        list.add("MySQL");
        list.remove("MySQL");
        System.out.println(list);

        MyArrayList<Integer> list2 = new MyArrayList<>();
        list2.add(23);
        list2.add(24);
        list2.add(25);
        list2.remove(25);
        System.out.println(list2);
    }
}

泛型方法

定义方法时同时定义了泛型的方法就是泛型方法。
泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
范例: public void show(T t) { }
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。

/**
    目标:自定义泛型方法。

    什么是泛型方法?
        定义了泛型的方法就是泛型方法。

    泛型方法的定义格式:
        修饰符 <泛型变量> 返回值类型 方法名称(形参列表){

        }
        注意:方法定义了是什么泛型变量,后面就只能用什么泛型变量。
        泛型类的核心思想:是把出现泛型变量的地方全部替换成传输的真实数据类型。

    需求:给你任何一个类型的数组,都能返回它的内容。Arrays.toString(数组)的功能!

    小结:
        泛型方法可以让方法更灵活的接收数据,可以做通用技术!
 */
public class GenericDemo {
    public static void main(String[] args) {
        String[] names = {"小璐", "蓉容", "小何"};
        printArray(names);

        Integer[] ages = {10, 20, 30};
        printArray(ages);

        Integer[] ages2 = getArr(ages);
        String[]  names2 = getArr(names);
    }

    public static <T> T[] getArr(T[] arr){
        return arr;
    }

    public static <T> void printArray(T[] arr){
        if(arr != null){
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0; i < arr.length; i++) {
                sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
            }
            sb.append("]");
            System.out.println(sb);
        }else {
            System.out.println(arr);
        }
    }
}

泛型接口

使用了泛型定义的接口就是泛型接口。
泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}
范例: public interface Data{}
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型

public interface Data<E> {
    void add(E e);
    void delete(int id);
    void update(E e);
    E queryById(int id);
}
public class StudentData implements Data<Student>{
    @Override
    public void add(Student student) {

    }

    @Override
    public void delete(int id) {

    }

    @Override
    public void update(Student student) {

    }

    @Override
    public Student queryById(int id) {
        return null;
    }
}

泛型通配符

通配符 ? 可以在“使用泛型”的时候代表一切类型。
E T K V 是在定义泛型的时候使用的。
? extends Car: ?必须是Car或者其子类 泛型上限
? super Car : ?必须是Car或者其父类 泛型下限

/**
    目标:泛型通配符。?

    需求:开发一个极品飞车的游戏,所有的汽车都能一起参与比赛。

    注意:
        虽然BMW和BENZ都继承了Car
        但是ArrayList<BMW>和ArrayList<BENZ>与ArrayList<Car>没有关系的!!
    通配符:?
        ?可以在“使用泛型”的时候代表一切类型。
        E T K V 是在定义泛型的时候使用的。
    泛型的上下限:
        ? extends Car : ?必须是Car或者其子类  泛型上限
        ? super Car :?必须是Car或者其父类   泛型下限
    小结:
        通配符:?
        ?可以在“使用泛型”的时候代表一切类型。

 */
public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        bmws.add(new BMW());
        go(bmws);

        ArrayList<BENZ> benzs = new ArrayList<>();
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        go(benzs);

        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());
        // go(dogs);
    }

    /**
       所有车比赛
     */
    public static void go(ArrayList<? extends Car> cars){
    }
}

class Dog{

}

class  BENZ extends Car{
}

class BMW extends Car{
}

// 父类
class Car{
}

Set系列

Set系列集合特点
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。

import java.util.HashSet;
import java.util.Set;
public class SetDemo1 {
    public static void main(String[] args) {
        // 看看Set系列集合的特点: HashSet LinkedHashSet TreeSet
        //
        Set<String> sets = new HashSet<>(); // 一行经典代码  无序不重复,无索引
        // Set<String> sets = new LinkedHashSet<>(); // 有序  不重复 无索引
        sets.add("MySQL");
        sets.add("MySQL");
        sets.add("Java");
        sets.add("Java");
        sets.add("HTML");
        sets.add("HTML");
        sets.add("SpringBoot");
        sets.add("SpringBoot");
        System.out.println(sets);
    }
}

Set集合的底层原理是什么样的
JDK8之前的,哈希表:底层使用数组+链表组成
JDK8开始后,哈希表:底层采用数组+链表+红黑树组成

哈希表的详细流程
1创建一个默认长度16,默认加载因为0.75的数组,数组名table
2根据元素的哈希值跟数组的长度计算出应存入的位置
3判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素, 则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。
4当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍

HashSet

HashSet去重复原理解析
1创建一个默认长度16的数组,数组名table
2根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
3判断当前位置是否为null,如果是null直接存入
4如果位置不为null,表示有元素,则调用equals方法比较
5如果一样,则不存,如果不一样,则存入数组
结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法

Example:
创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,要求:学生对象的成员变量值相同,我们就认为是同一个对象
定义学生类,创建HashSet集合对象, 创建学生对象
把学生添加到集合
在学生类中重写两个方法,hashCode()和equals(),自动生成即可
遍历集合(增强for)

//Student.java
package com.itheima.d1_collection_set;

import java.util.Objects;

public class  Student {
    private String name;
    private int age;
    private char sex;

    public Student() {
    }

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

    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;
    }

    public char getSex() {
        return sex;
    }

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

    /**
       只要2个对象内容一样,结果一定是true
     * @param o
     * @return
     */
    @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 && sex == student.sex && Objects.equals(name, student.name);
    }

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

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

/**
    目标:让Set集合把重复内容的对象去掉一个(去重复)
 */
public class SetDemo3 {
    public static void main(String[] args) {
        // Set集合去重复原因:先判断哈希值算出来的存储位置是否相同 再判断equals
        Set<Student> sets = new HashSet<>();

        Student s1 = new Student("无恙", 20, '男');
        Student s2 = new Student("无恙", 20, '男');
        Student s3 = new Student("周雄", 21, '男');
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s3.hashCode());

        sets.add(s1);
        sets.add(s2);
        sets.add(s3);

        System.out.println(sets);
    }
}
//如果不重写hashCode()函数则s1,s2hashcode不同,无法判定为重复。
//Alt+insert快速自动生成hashcode()和equals()函数

HashSet遍历

public static void main(String[] args){
		
		Set<String> set = new HashSet<String>();
		set.add("a1");
		set.add("b2");
		set.add("c3");
		set.add("d4");
		
		// 初始化一个HashSet集合
		System.out.println("--------原HashSet集合----------");
		System.out.println(set);
		System.out.println();
		
		// 方法一:
		Iterator<String> iterator = set.iterator();
		System.out.println("--------迭代器遍历HashSet----------");
		while(iterator.hasNext()){
			System.out.print(iterator.next()+",");
		}
		
		
		// 方法二:
		System.out.println("--------加强for循环遍历---------");
		for (String item : set) {
			System.out.print(item+",");
		}
	}

LinkedHashSet

LinkedHashSet集合概述和特点
有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。

TreeSet

TreeSet集合概述和特点
不重复、无索引、可排序
可排序:按照元素的大小默认升序(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。

注意区分有序与排序!

TreeSet集合默认的规则
对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet无法直接排序。
结论:想要使用TreeSet存储自定义类型,需要制定排序规则

TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则
方式一
让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二
TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
两种方式中,关于返回值的规则:
如果认为第一个元素大于第二个元素返回正整数即可。
如果认为第一个元素小于第二个元素返回负整数即可。
如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序

public class Apple implements Comparable<Apple>{
    private String name;
    private String color;
    private double price;
    private int weight;

    public Apple() {
    }

    public Apple(String name, String color, double price, int weight) {
        this.name = name;
        this.color = color;
        this.price = price;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", price=" + price +
                ", weight=" + weight +
                '}';
    }

    /**
      方式一:类自定义比较规则
      o1.compareTo(o2)
     * @param o
     * @return
     */
    @Override
    public int compareTo(Apple o) {
        // 按照重量进行比较的
        return this.weight - o.weight ; // 去重重量重复的元素
        // return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素
    }
}

/**
   目标:观察TreeSet对于有值特性的数据如何排序。
        学会对自定义类型的对象进行指定规则排序
 */
public class  SetDemo5 {
    public static void main(String[] args) {
        Set<Integer> sets = new TreeSet<>(); // 不重复 无索引 可排序
        sets.add(23);
        sets.add(24);
        sets.add(12);
        sets.add(8);
        System.out.println(sets);

        Set<String> sets1 = new TreeSet<>(); // 不重复 无索引 可排序
        sets1.add("Java");
        sets1.add("Java");
        sets1.add("angela");
        sets1.add("黑马");
        sets1.add("Java");
        sets1.add("About");
        sets1.add("Python");
        sets1.add("UI");
        sets1.add("UI");
        System.out.println(sets1);

        System.out.println("------------------------------");
        // 方式二:集合自带比较器对象进行规则定制
        //
//        Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {
//            @Override
//            public int compare(Apple o1, Apple o2) {
//                // return o1.getWeight() - o2.getWeight(); // 升序
//                // return o2.getWeight() - o1.getWeight(); // 降序
//                // 注意:浮点型建议直接使用Double.compare进行比较
//                // return Double.compare(o1.getPrice() , o2.getPrice()); // 升序
//                return Double.compare(o2.getPrice() , o1.getPrice()); // 降序
//            }
//        });

        Set<Apple> apples = new TreeSet<>(( o1,  o2) ->  Double.compare(o2.getPrice() , o1.getPrice())  );
        apples.add(new Apple("红富士", "红色", 9.9, 500));
        apples.add(new Apple("青苹果", "绿色", 15.9, 300));
        apples.add(new Apple("绿苹果", "青色", 29.9, 400));
        apples.add(new Apple("黄苹果", "黄色", 9.8, 500));
        System.out.println(apples);
    }
}

三种Set遍历比较

public static void main(String[] args) {
    // HashSet, LinkedHashSet
    List<String> list = new LinkedList<>();
    list.add("江");
    list.add("山");
    list.add("如");
    list.add("此");
    list.add("多");
    list.add("娇");

    // TreeSet, 因为使用汉字的话,不容易看出TreeSet的排序效果
    List<String> list01 = new LinkedList<>();
    list01.add("J");
    list01.add("S");
    list01.add("R");
    list01.add("C");
    list01.add("D");
    list01.add("J");

    HashSet<String> set = new HashSet<>(list);
    System.out.println("----------------------HashSet----------------------");
    // for循环遍历方式
    for (String s : set){
        //System.out.println(s);
    }
    // 迭代器遍历方式
    Iterator<String> ite = set.iterator();
    while(ite.hasNext()){
        System.out.println(ite.next());
    }
    // foreach遍历方式
    set.forEach(ites->{
        //System.out.println(ites);
    });

    LinkedHashSet<String> set01 = new LinkedHashSet<>(list);
    System.out.println("-------------------LinkedHashSet-------------------");
    //for循环遍历方式
    for (String s : set01){
        //System.out.println(s);
    }
    // 迭代器遍历方式
    Iterator<String> ite01 = set01.iterator();
    while(ite01.hasNext()){
        System.out.println(ite01.next());
    }
    // foreach遍历方式
    set01.forEach(ites->{
        //System.out.println(ites);
    });

    TreeSet<String> set02 = new TreeSet<>(list01);
    System.out.println("----------------------TreeSet----------------------");
    // for循环遍历方式
    for (String s : set02){
        //System.out.println(s);
    }
    // 迭代器遍历方式
    Iterator<String> ite02 = set02.iterator();
    while(ite02.hasNext()){
        System.out.println(ite02.next());
    }
    // foreach遍历方式
    set02.forEach(ites->{
        //System.out.println(ites);
    });
}

//运行结果

----------------------HashSet----------------------
山
如
此
娇
多
江
-------------------LinkedHashSet-------------------
江
山
如
此
多
娇
----------------------TreeSet----------------------
C
D
J
R
S

Process finished with exit code 0

可变参数

import java.util.Arrays;

/**
    目标:可变参数。

    可变参数用在形参中可以接收多个数据。
    可变参数的格式:数据类型...参数名称

    可变参数的作用:
         传输参数非常灵活,方便。
         可以不传输参数。
         可以传输一个参数。
         可以传输多个参数。
         可以传输一个数组。

     可变参数在方法内部本质上就是一个数组。
     可变参数的注意事项:
         1.一个形参列表中可变参数只能有一个!!
         2.可变参数必须放在形参列表的最后面!!
     小结:
        记住。
 */
public class MethodDemo {
    public static void main(String[] args) {

        sum(); // 1、不传参数
        sum(10); // 2、可以传输一个参数
        sum(10, 20, 30); // 3、可以传输多个参数
        sum(new int[]{10, 20, 30, 40, 50}); // 4、可以传输一个数组
    }

    /**
       注意:一个形参列表中只能有一个可变参数,可变参数必须放在形参列表的最后面
     * @param nums
     */
    public static void sum(  int...nums){
        // 注意:可变参数在方法内部其实就是一个数组。 nums
        System.out.println("元素个数:" + nums.length);
        System.out.println("元素内容:" + Arrays.toString(nums));
    }
}

Collections工具类

ava.utils.Collections:是集合工具类
作用:Collections并不属于集合,是用来操作集合的工具类。

Collections常用的API

方法名称说明
public static <T> boolean addAll(Collection<? super T> c, T... elements)给集合对象批量添加元素
public static void shuffle(List<?> list) 打乱List集合元素的顺序
方法名称说明
public static <T> void sort(List<T> list)将集合中元素按照默认规则排序
public static void sort(List list,Comparator c)将集合中元素按照指定规则排序

tips:Shift+F6同步改变量名

/**
    目标:Collections工具类的使用。

    java.utils.Collections:是集合工具类
    Collections并不属于集合,是用来操作集合的工具类。
    Collections有几个常用的API:
         - public static <T> boolean addAll(Collection<? super T> c, T... elements)
             给集合对象批量添加元素!
         - public static void shuffle(List<?> list) :打乱集合顺序。
         - public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
         - public static <T> void sort(List<T> list,Comparator<? super T> c):将集合中元素按照指定规则排序。
 */
public class CollectionsDemo01 {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        //names.add("楚留香");
        //names.add("胡铁花");
        //names.add("张无忌");
        //names.add("陆小凤");
        Collections.addAll(names, "楚留香","胡铁花", "张无忌","陆小凤");
        System.out.println(names);

        // 2、public static void shuffle(List<?> list) :打乱集合顺序。
        Collections.shuffle(names);
        System.out.println(names);

        // 3、 public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。 (排值特性的元素)
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 12, 23, 2, 4);
        System.out.println(list);
        Collections.sort(list);
        System.out.println(list);
    }
}
//Apple.java
public class Apple implements Comparable<Apple>{
    private String name;
    private String color;
    private double price;
    private int weight;

    public Apple() {
    }

    public Apple(String name, String color, double price, int weight) {
        this.name = name;
        this.color = color;
        this.price = price;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", price=" + price +
                ", weight=" + weight +
                '}';
    }

    /**
      方式一:类自定义比较规则
      o1.compareTo(o2)
     * @param o
     * @return
     */
    @Override
    public int compareTo(Apple o) {
        // 按照重量进行比较的
        return this.weight - o.weight ; // List集存储相同大小的元素 会保留!
    }
}
//CollectionsDemo02.java
import java.util.*;

/**
    目标:引用数据类型的排序。

    字符串按照首字符的编号升序排序!

    自定义类型的比较方法API:Collections
         - public static <T> void sort(List<T> list):
               将集合中元素按照默认规则排序。
               对于自定义的引用类型的排序人家根本不知道怎么排,直接报错!

         - public static <T> void sort(List<T> list,Comparator<? super T> c):
                将集合中元素按照指定规则排序,自带比较器
 */
public class CollectionsDemo02 {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>(); // 可以重复!
        apples.add(new Apple("红富士", "红色", 9.9, 500));
        apples.add(new Apple("青苹果", "绿色", 15.9, 300));
        apples.add(new Apple("绿苹果", "青色", 29.9, 400));
        apples.add(new Apple("黄苹果", "黄色", 9.8, 500));

//        Collections.sort(apples); // 方法一:可以的,Apple类已经重写了比较规则
//        System.out.println(apples);

        // 方式二:sort方法自带比较器对象
//        Collections.sort(apples, new Comparator<Apple>() {
//            @Override
//            public int compare(Apple o1, Apple o2) {
//                return Double.compare(o1.getPrice() , o2.getPrice()); // 按照价格排序!!
//            }
//        });

        Collections.sort(apples, ( o1,  o2) ->  Double.compare(o1.getPrice() , o2.getPrice()) );
        System.out.println(apples);

    }
}

Example:斗地主游戏

需求:在启动游戏房间的时候,应该提前准备好54张牌,完成洗牌、发牌、牌排序、逻辑。
分析:
1当系统启动的同时需要准备好数据的时候,就可以用静态代码块了
2洗牌就是打乱牌的顺序
3定义三个玩家、依次发出51张牌
4给玩家的牌进行排序(拓展)
5输出每个玩家的牌数据

//card.java
public class Card {
    private String size;
    private String color;
    private int index; // 牌的真正大小

    public Card(){
    }

    public Card(String size, String color, int index) {
        this.size = size;
        this.color = color;
        this.index = index;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    @Override
    public String toString() {
        return size + color;
    }
}
//GameDemo.java
import java.util.*;

/**
    目标:斗地主游戏的案例开发。

    业务需求分析:
        斗地主的做牌, 洗牌, 发牌, 排序(拓展知识), 看牌。
        业务: 总共有54张牌。
        点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
        花色: "♠", "♥", "♣", "♦"
        大小王: "👲" , "🃏"
        点数分别要组合4种花色,大小王各一张。
        斗地主:发出51张牌,剩下3张作为底牌。

    功能:
        1.做牌。
        2.洗牌。
        3.定义3个玩家
        4.发牌。
        5.排序(拓展,了解,作业)
        6.看牌
 */
public class GameDemo {
    /**
      1、定义一个静态的集合存储54张牌对象
     */
     public static List<Card> allCards = new ArrayList<>();

    /**
      2、做牌:定义静态代码块初始化牌数据
     */
    static {
        // 3、定义点数:个数确定,类型确定,使用数组
        String[] sizes = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        // 4、定义花色:个数确定,类型确定,使用数组
        String[] colors = {"♠", "♥", "♣", "♦"};
        // 5、组合点数和花色
        int index = 0; // 记录牌的大小
        for (String size : sizes) {
            index++;
            for (String color : colors) {
                // 6、封装成一个牌对象。
                Card c = new Card(size, color, index);
                // 7、存入到集合容器中去
                allCards.add(c);
            }
        }
        // 8 大小王存入到集合对象中去 "👲" , "🃏"
        Card c1 = new Card("" ,  "🃏", ++index);
        Card c2 = new Card("" ,  "👲",++index);
        Collections.addAll(allCards , c1 , c2);
        System.out.println("新牌:" + allCards);
    }

    public static void main(String[] args) {
        // 9、洗牌
        Collections.shuffle(allCards);
        System.out.println("洗牌后:" + allCards);

        // 10、发牌(定义三个玩家,每个玩家的牌也是一个集合容器)
        List<Card> linhuchong = new ArrayList<>();
        List<Card> jiumozhi = new ArrayList<>();
        List<Card> renyingying = new ArrayList<>();

        // 11、开始发牌(从牌集合中发出51张牌给三个玩家,剩余3张作为底牌)
        // allCards = [🃏, A♠, 5♥, 2♠, 2♣, Q♣, 👲, Q♠ ...
        //    i        0  1   2   3   4   5    6  7      %  3
        for (int i = 0; i < allCards.size() - 3; i++) {
            // 先拿到当前牌对象
            Card c = allCards.get(i);
            if(i % 3 == 0) {
                // 请阿冲接牌
                linhuchong.add(c);
            }else if(i % 3 == 1){
                // 请阿鸠
                jiumozhi.add(c);
            }else if(i % 3 == 2){
                // 请盈盈接牌
                renyingying.add(c);
            }
        }

        // 12、拿到最后三张底牌(把最后三张牌截取成一个子集合)
        List<Card> lastThreeCards = allCards.subList(allCards.size() - 3 , allCards.size());

        // 13、给玩家的牌排序(从大到小 可以自己先试试怎么实现)
        sortCards(linhuchong);
        sortCards(jiumozhi);
        sortCards(renyingying);

        // 14、输出玩家的牌:
        System.out.println("啊冲:" + linhuchong);
        System.out.println("啊鸠:" + jiumozhi);
        System.out.println("盈盈:" + renyingying);
        System.out.println("三张底牌:" + lastThreeCards);
    }

    /**
       给牌排序
     * @param cards
     */
    private static void sortCards(List<Card> cards) {
        // cards = [J♥, A♦, 3♥, 🃏, 5♦, Q♥, 2♥
        Collections.sort(cards, new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                // o1 = J♥
                // o2 = A♦
                // 知道牌的大小,才可以指定规则
                return o2.getIndex() - o1.getIndex();
            }
        });
    }

}

Map

Map集合是一种双列集合,每个元素包含两个数据。
Map集合的每个元素的格式:key=value(键值对元素)。
Map集合也被称为“键值对集合”
Collection集合的格式: [元素1,元素2,元素3..]
Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , …}

Map集合体系特点:
Map集合的特点都是由键决定的。
Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
Map集合后面重复的键对应的值会覆盖前面重复键的值。
Map集合的键值对都可以为null

Map集合实现类特点:
HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
TreeMap:元素按照建是排序,不重复,无索引的,值不做要求

Map集合
Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的

方法名称说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
Map API
/**
    目标:Map集合的常用API(重点中的重点)
     - public V put(K key, V value):  把指定的键与指定的值添加到Map集合中。
     - public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
     - public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
     - public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
     - public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
     - public boolean containKey(Object key):判断该集合中是否有此键。
     - public boolean containValue(Object value):判断该集合中是否有此值。
 */
public class MapDemo {
    public static void main(String[] args) {
        // 1.添加元素: 无序,不重复,无索引。
        Map<String , Integer> maps = new HashMap<>();
        maps.put("iphoneX",10);
        maps.put("娃娃",20);
        maps.put("iphoneX",100);//  Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!
        maps.put("huawei",100);
        maps.put("生活用品",10);
        maps.put("手表",10);
        // {huawei=100, 手表=10, 生活用品=10, iphoneX=100, 娃娃=20}
        System.out.println(maps);

        // 2.清空集合
//        maps.clear();
//        System.out.println(maps);

        // 3.判断集合是否为空,为空返回true ,反之!
        System.out.println(maps.isEmpty());

        // 4.根据键获取对应值:public V get(Object key)
        Integer key = maps.get("huawei");
        System.out.println(key);
        System.out.println(maps.get("生活用品")); // 10
        System.out.println(maps.get("生活用品2")); // null

        // 5.根据键删除整个元素。(删除键会返回键的值)
        System.out.println(maps.remove("iphoneX"));
        System.out.println(maps);

        // 6.判断是否包含某个键 ,包含返回true ,反之
        System.out.println(maps.containsKey("娃娃"));  // true
        System.out.println(maps.containsKey("娃娃2"));  // false
        System.out.println(maps.containsKey("iphoneX")); // false

        // 7.判断是否包含某个值。
        System.out.println(maps.containsValue(100));  //
        System.out.println(maps.containsValue(10));  //
        System.out.println(maps.containsValue(22)); //

        // {huawei=100, 手表=10, 生活用品=10, 娃娃=20}
        // 8.获取全部键的集合:public Set<K> keySet()
        Set<String> keys = maps.keySet();
        System.out.println(keys);

        System.out.println("------------------------------");
        // 9.获取全部值的集合:Collection<V> values();
        Collection<Integer> values = maps.values();
        System.out.println(values);

        // 10.集合的大小
        System.out.println(maps.size()); // 4

        // 11.合并其他Map集合。(拓展)
        Map<String , Integer> map1 = new HashMap<>();
        map1.put("java1", 1);
        map1.put("java2", 100);
        Map<String , Integer> map2 = new HashMap<>();
        map2.put("java2", 1);
        map2.put("java3", 100);
        map1.putAll(map2); // 把集合map2的元素拷贝一份到map1中去
        System.out.println(map1);
        System.out.println(map2);
    }
}

Map集合的遍历方式有:3种
方式一:键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大
方式三:JDK 1.8开始之后的新技术:Lambda表达式

/*-----------------------遍历方式1-------------------------*/
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
    目标:Map集合的遍历方式一:键找值

    Map集合的遍历方式有:3种。
        (1)“键找值”的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
        (2)“键值对”的方式遍历:难度较大。
        (3)JDK 1.8开始之后的新技术:Lambda表达式。(暂时了解)

    a.“键找值”的方式遍历Map集合。
        1.先获取Map集合的全部键的Set集合。
        2.遍历键的Set集合,然后通过键找值。
    小结:
        代码简单,需要记住!

 */
public class MapDemo01 {
    public static void main(String[] args) {
        Map<String , Integer> maps = new HashMap<>();
        // 1.添加元素: 无序,不重复,无索引。
        maps.put("娃娃",30);
        maps.put("iphoneX",100);
        maps.put("huawei",1000);
        maps.put("生活用品",10);
        maps.put("手表",10);
        System.out.println(maps);
        // maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}

        // 1、键找值:第一步:先拿到集合的全部键。
        Set<String> keys = maps.keySet();
        // 2、第二步:遍历每个键,根据键提取值
        for (String key : keys) {
            int value = maps.get(key);
            System.out.println(key + "===>" + value);
        }

    }
}
/*-----------------------遍历方式2-------------------------*/
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
    目标:Map集合的遍历方式。

    Map集合的遍历方式有:3种。
        (1)“键找值”的方式遍历:先获取Map集合全部的键,再根据键找值。
        (2)“键值对”的方式遍历:难度较大。
        (3)JDK 1.8开始之后的新技术:Lambda表达式。

    b.“键值对”的方式遍历:
        1.把Map集合转换成一个Set集合:Set<Map.Entry<K, V>> entrySet();
        2.此时键值对元素的类型就确定了,类型是键值对实体类型:Map.Entry<K, V>
        3.接下来就可以用foreach遍历这个Set集合,类型用Map.Entry<K, V>
 */
public class MapDemo02 {
    public static void main(String[] args) {
        Map<String , Integer> maps = new HashMap<>();
        // 1.添加元素: 无序,不重复,无索引。
        maps.put("娃娃",30);
        maps.put("iphoneX",100);
        maps.put("huawei",1000);
        maps.put("生活用品",10);
        maps.put("手表",10);
        System.out.println(maps);
        // maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
        /**
            maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}
                👇
            使用foreach遍历map集合.发现Map集合的键值对元素直接是没有类型的。所以不可以直接foreach遍历集合。
                👇
            可以通过调用Map的方法 entrySet把Map集合转换成Set集合形式  maps.entrySet();
                👇
            Set<Map.Entry<String,Integer>> entries =  maps.entrySet();
             [(huawei=1000), (手表=10), (生活用品=10), (iphoneX=100), (娃娃=30)]
                              entry
                👇
            此时可以使用foreach遍历
       */
       // 1、把Map集合转换成Set集合
        Set<Map.Entry<String, Integer>> entries = maps.entrySet();
        // 2、开始遍历
        for(Map.Entry<String, Integer> entry : entries){
            String key = entry.getKey();
            int value = entry.getValue();
            System.out.println(key + "====>" + value);
        }
    }
}
/*-----------------------遍历方式3-------------------------*/
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

/**
    目标:Map集合的遍历方式。

    Map集合的遍历方式有:3种。
        (1)“键找值”的方式遍历:先获取Map集合全部的键,再根据键找值。
        (2)“键值对”的方式遍历:难度较大。
        (3)JDK 1.8开始之后的新技术:Lambda表达式。

    c.JDK 1.8开始之后的新技术:Lambda表达式。(暂时了解)
 */
public class MapDemo03 {
    public static void main(String[] args) {
        Map<String , Integer> maps = new HashMap<>();
        // 1.添加元素: 无序,不重复,无索引。
        maps.put("娃娃",30);
        maps.put("iphoneX",100);//  Map集合后面重复的键对应的元素会覆盖前面重复的整个元素!
        maps.put("huawei",1000);
        maps.put("生活用品",10);
        maps.put("手表",10);
        System.out.println(maps);

        //  maps = {huawei=1000, 手表=10, 生活用品=10, iphoneX=100, 娃娃=30}

//        maps.forEach(new BiConsumer<String, Integer>() {
//            @Override
//            public void accept(String key, Integer value) {
//                System.out.println(key + "--->" + value);
//            }
//        });

        maps.forEach((k, v) -> {
                System.out.println(k + "--->" + v);
        });

    }
}

Example:Map集合案例-统计投票人数
需求
某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
分析
将80个学生选择的数据拿到程序中去。
定义Map集合用于存储最终统计的结果。
遍历80个学生选择的数据,看Map集合中是否存在,不存在存入“数据=1“,存在则其对应值+1

/**
   需求:统计投票人数
 */
public class MapTest1 {
    public static void main(String[] args) {
         // 1、把80个学生选择的数据拿进来。
        String[] selects = {"A" , "B", "C", "D"};
        StringBuilder sb = new StringBuilder();
        Random r = new Random();
        for (int i = 0; i < 80; i++) {
            sb.append(selects[r.nextInt(selects.length)]);
        }
        System.out.println(sb);

        // 2、定义一个Map集合记录最终统计的结果: A=30 B=20 C=20 D=10  键是景点 值是选择的数量
        Map<Character, Integer> infos = new HashMap<>(); //

        // 3、遍历80个学生选择的数据
        for (int i = 0; i < sb.length(); i++) {
            // 4、提取当前选择景点字符
            char ch = sb.charAt(i);
            // 5、判断Map集合中是否存在这个键
            if(infos.containsKey(ch)){
                 // 让其值 + 1
                infos.put(ch , infos.get(ch) + 1);
            }else {
                // 说明此景点是第一次被选
                infos.put(ch , 1);
            }
        }

        // 4、输出集合
        System.out.println(infos);

    }
}

HashMap的特点
HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已
实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
依赖hashCode方法和equals方法保证键的唯一
如果键要存储的是自定义对象,需要重写hashCode和equals方法。

LinkedHashMap集合概述和特点
由键决定:有序、不重复、无索引。
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序

/**
    目标:认识Map体系的特点:按照键无序,不重复,无索引。值不做要求。
 */
public class LinkedHashMapDemo2 {
    public static void main(String[] args) {
        // 1、创建一个Map集合对象
        Map<String, Integer> maps = new LinkedHashMap<>();
        maps.put("鸿星尔克", 3);
        maps.put("Java", 1);
        maps.put("枸杞", 100);
        maps.put("Java", 100); // 覆盖前面的数据
        maps.put(null, null);
        System.out.println(maps);

    }
}
/*-------输出-------------*/
{鸿星尔克=3, Java=100, 枸杞=100, null=null}

TreeMap集合概述和特点:
由键决定特性:不重复、无索引、可排序
可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
TreeMap跟TreeSet一样底层原理是一样的
TreeMap集合自定义排序规则有2种:
类实现Comparable接口,重写比较规则
集合自定义Comparator比较器对象,重写比较规则

import com.itheima.d1_collection_set.Student;
import java.util.HashMap;
import java.util.Map;

public class HashMapDemo1 {
    public static void main(String[] args) {
         // Map集合是根据键去除重复元素
        Map<Student, String> maps = new HashMap<>();

        Student s1 = new Student("无恙", 20, '男');
        Student s2 = new Student("无恙", 20, '男');
        Student s3 = new Student("周雄", 21, '男');

        maps.put(s1, "北京");
        maps.put(s2, "上海");
        maps.put(s3, "广州");

        System.out.println(maps);
    }
}
/*------------输出-------------*/
{Student{name='无恙', age=20, sex=男}=上海, Student{name='周雄', age=21, sex=男}=广州}

集合嵌套

Map集合案例-统计投票人数(集合的嵌套)
需求:
某个班级多名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生可以选择多个景点,请统计出最终哪个景点想去的人数最多。
分析:
将80个学生选择的数据拿到程序中去,需要记住每个学生选择的情况。
定义Map集合用于存储最终统计的结果。

import java.util.*;

/**
   需求:统计投票人数
 */
public class MapTest4 {
    public static void main(String[] args) {
        // 1、要求程序记录每个学生选择的情况。
        // 使用一个Map集合存储。
        Map<String, List<String>> data = new HashMap<>();

        // 2、把学生选择的数据存入进去。
        List<String> selects = new ArrayList<>();
        Collections.addAll(selects, "A", "C");
        data.put("罗勇", selects);

        List<String> selects1 = new ArrayList<>();
        Collections.addAll(selects1, "B", "C" , "D");
        data.put("胡涛", selects1);

        List<String> selects2 = new ArrayList<>();
        Collections.addAll(selects2 , "A",  "B", "C" , "D");
        data.put("刘军", selects2);

        System.out.println(data);

        // 3、统计每个景点选择的人数。
        Map<String, Integer> infos = new HashMap<>(); // {}

        // 4、提取所有人选择的景点的信息。
        Collection<List<String>> values = data.values();
        System.out.println(values);
        // values = [[A, B, C, D], [B, C, D], [A, C]]
        //             value

        for (List<String> value : values) {
            for (String s : value) {
                // 有没有包含这个景点
                if(infos.containsKey(s)){
                    infos.put(s, infos.get(s) + 1);
                }else {
                    infos.put(s , 1);
                }
            }
        }

        System.out.println(infos);
    }
}
届ける言葉を今は育ててる
最后更新于 2023-02-28