java小白第三天

4 篇文章 0 订阅
2 篇文章 0 订阅

写在前面

本文是边看黑马b站视频边写的一片笔记, 文中大多图片都来自黑马视频. 旨在巩固学习以及方便后续查阅和供广大朋友们学习, 感谢黑马视频分享

01 Lambda

在这里插入图片描述
函数式编程

Functional Programming
关注点是方法, 不是对象
是jdk8之后的一种新语法形式, 简化函数式接口的匿名内部类的写法

01 标准格式

() -> {

}

+ () 对应着方法的形参
+ -> 固定格式
+ {} 对应着方法的方法体

注意事项

  1. Lambda 表达式可以用来简化匿名内部类的书写
  2. Lambda 表达式只能简化函数时接口的匿名内部类
  3. 函数式接口: 有且仅有一个抽象方法的接口叫函数式接口, 注解 @FunctionalInterface
  4. lambda表达式中相当于局部内部类,局部内部类访问外部方法中局部变量时,局部变量会默认加final关键字, 所以在 Lambda 方法体内可以访问 外部方法/类 中的变量, 但不可赋值

02 Lambda 省略写法 – 可推导, 可省略

Lambda 的省略规则:

  1. 参数类型可以省略不写
  2. 如果只有一个参数, 参数类型可以省略, () 也可以省略
  3. 如果 Lambda 表达式的方法体只有一行, 大括号, 分号, return 可以省略不写, 但是需要同时省略

02 集合进阶

在这里插入图片描述

  • 集合体系结构
  • Collection集合
  • List集合
  • ArrayList集合
  • LinkedList集合

01 集合体系结构

  • 单列集合 Collection : 单列集合的主接口, 所有单列集合的都继承了它的功能
    • List : 添加元素 有序(存取有序) 可重复 有索引
      • ArrayList
      • LinkedList
      • Vector – 淘汰
    • Set : 添加元素 无序(存取不一致) 不重复 无索引
      • HashSet
        • LinkedHashSet
      • TreeSet
  • 双列集合

在这里插入图片描述

02 Collection

collection 是单列集合的主接口, 所有单列集合都直接或间接继承于 Collection
在这里插入图片描述
note

  1. contains() 方法底层通过调用对象的 equals() 方法进行判断, 所以存储自定义对象时, 需要重写 equals() 方法

01 Collection 遍历

01 迭代器遍历 Iterator

迭代器不依赖索引, 是集合专用的遍历方式
在这里插入图片描述
note

  1. iterator() 是集合获取迭代器的方法

遍历示例
在这里插入图片描述
注意细节

  1. 迭代器遍历完毕, 不会复位, 所以再次遍历时, 只能再获取一个迭代器
  2. 迭代器遍历时, 不能用集合的方法进行集合中元素的增加或删除
    • 可以用迭代器的 remove 方法, 进行删除
02 增强for遍历
  • 增强 for 的底层就是迭代器, 为了简化迭代器的代码书写的
  • jdk5之后出现的, 其内部原理就是一个 Iterator 迭代器
  • 所有的单列集合和数组才能用增强 for 进行遍历

增强 for 格式

for (元素的数据类型 变量名 : 数组或者集合){
	/*
		修改变量名的内容, 对集合中的元素无影响
	*/
}for (String s : list){
	sout(s);
}
03 Lambda表达式遍历方式

在这里插入图片描述

代码示例
在这里插入图片描述

02 总结

在这里插入图片描述

03 Lsit

有序
可重复
有索引 – 特有的

在这里插入图片描述
lsit remove 方法的细节

public static void testListRemove(){
        /*
        需要强调的是:
            list集合有两个重载的remove方法
                1. remove(Object o) 删除指定对象
                2. remove(Integer index) 删除指定索引的对象
            当list集合中存储的元素是 Integer 时, 调用方法 remove 时, java会默认调用第二个方法
            这是因为在调用方法时, 如果方法出现了重载, 优先调用, 实参与形参一致的方法

            所以, 当需要指定对象删除集合中的 Integer 元素时, 可以使用手动装箱, 将参数包装为 Integer 类
         */

        List<Integer> list = new ArrayList<>();

        list.add(1);
        list.add(2);
        list.add(3);

        list.remove(1); // 默认删除索引为1处的元素
        System.out.println(list); // [1, 3]


        Integer i = Integer.valueOf(1); // 将 int 1 包装为 Integer 对象
        list.remove(i); // 这里删除的是对象为1的元素
        System.out.println(list); // [3]
    }

Lsit 集合的五种遍历
在这里插入图片描述
列表迭代器 – 代码示例
在这里插入图片描述
在这里插入图片描述

04 (补充)数据结构

在这里插入图片描述

讲个笑话
在这里插入图片描述

  • 栈: 先进后出, 后进先出

  • 队列: 先进先出, 后进后出

  • 数组: 物理存储空间连续

    • 查询效率块
    • 删除 添加效率低
  • 链表: 链表中的节点是独立的对象, 早内存中是不连续存储的, 每个节点一般包含数据值和下一个节点的地址

    • 查询慢
    • 增删快
  • 树:
    在这里插入图片描述
    二叉树节点的内部结构
    在这里插入图片描述

    • 二叉查找树 (二叉排序树或二叉查找树)
      • 每个节点上最多有两个子节点
      • 任意节点左子树上的节点值都小于当前节点
      • 任意节点右子树上的节点值都大于当前节点
    • 添加节点规则
      • 小左, 大右, 一样不要
    • 二叉树遍历方式
      • 前序遍历: 根左右
      • 中序遍历: 左根右 (从小到大)
      • 后序遍历: 左右根
      • 层序遍历
    • 二叉查找数弊端 – 节点的左右子树高度差过大, 导致查询效率过低
    • 所以: 二叉平衡树 – 要求: 任意节点的左右子树高度差不能超过1
      • 二叉平衡树的旋转机制
        • 确定支点: 从添加的节点开始, 不断的往父节点找不平衡的节点
        • 左旋或右旋即好
          • 左左: 当根节点左子树的左子树有节点插入, 导致二叉树不平衡 – 一次右旋
          • 左右: 当根节点左子树的右子树有节点插入, 导致二叉树不平衡 – 先局部左旋, 再整体右旋
          • 右右: 当根节点右子树的右子树有节点插入, 导致二叉树不平衡 – 一次左旋
          • 右左: 当根节点右子树的左子树有节点插入, 导致二叉树不平衡 – 先局部右旋, 再整体左旋
    • 红黑树
      • 红黑树是一种自平衡的二叉查找树, 是计算机科学中用到的一种数据结构
      • 1972年出现, 称之为平衡二叉B树 1978 修改为红黑树
      • 是一种特殊得二叉查找树, 红黑树的每一个节点上都有存储为表示节点的颜色
      • 每一个节点可以是红或者黑, 红黑树不是高度平衡的, 它的平衡是通过红黑规则进行实现的
        在这里插入图片描述
      • 红黑规则
        在这里插入图片描述
        1. 树节点数据结构在这里插入图片描述

        2. 添加节点默认为红色(效率高)

        3. 添加节点详细规则如下 在这里插入图片描述

        4. 红黑树的增删改查性能都很好

05 ArrayLsit

底层是一个数组

01 ArrayList 底层原理

在这里插入图片描述
扩容时, 创建一个新数组, 然后将就数组的内容放到新数组中

在这里插入图片描述
ArrayList 底层源码 – jdk17
在这里插入图片描述

06 LinkedList

LinkedList 底层数据结构是双链表, 查询慢, 增删快, 尤其是首尾元素的操作
LinkedList 本身有很多特有的直接操作首尾元素的 API

源码理解

  1. NodeLinkedList 的内部类
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

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

在这里插入图片描述
在这里插入图片描述
LinkedList add 底层源码

在这里插入图片描述

07 Iterator

Iterator 源码分析
在这里插入图片描述


08 Set

无序, 不重复(去重), 无索引

Set 接口中的方法基本上与 Collection 的API一致

实现类

  • HashSet 无序 不重复 无索引
  • LinkedHashSet 有序 不重复 无索引
  • TreeSet 可排序 不重复 无索引

注意事项

  1. 使用 add 方法时, 一般需要对其返回值进行判断, 因为 Set 集合是不允许重复的

01 遍历方式

01 迭代器

02 增强for

03 Lambda表达式

代码示例

// set 遍历
    public static void testSeeAll(){

        // 创建 set 集合
        Set<String> s = new HashSet<>();

        if (!s.add("aaa")){
            System.out.println("worry");
        }

        if (!s.add("bbb")){
            System.out.println("worry");
        }

        if (!s.add("ccc")){
            System.out.println("worry");
        }

        System.out.println(s);

        // 遍历

        Iterator<String> iterator = s.iterator();

        while(iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
        }

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

        // Lambda
        s.forEach(str -> System.out.println(str));
        
        // method reference
        s.forEach(System.out::println);
    }

09 HashSet

  • 底层采取哈希表存取数据
  • 哈希表是一种对应增删改查数据性能都较好的结构

哈希表

  • jdk8前: 数组 + 链表
  • jdk8开始: 数组 + 链表 + 红黑树

哈希值

  • 是根据 hashCode 方法计算出来的 int 类型的整数
  • 该方法定义在 Object 类中, 所有对象都可调用, 默认使用地址值进行计算
  • 一般情况下, 需要重写 hashCode 方法, 利用对象内部的属性值计算哈希值

对象的哈希值特点

  • 如果没有重写 hashCode 方法, 不同对象计算出的哈希值是不同的, 这是因为 Object 默认使用对象的地址值计算哈希值
  • 如果已经重写 hashCode 方法, 不同的对象只要属性值相同, 计算出的哈希值就是一样的
  • 在小部分的情况下, 不同的属性值或不同的地址值计算出的哈希值也有可能一样 (哈希碰撞 – 概率很小)

01 哈希表底层原理

01
在这里插入图片描述
02
![在这里插入图片描述](https://img-blog.csdnimg.cn/d39ef486dcef4a7f865b35f6499ce7d0.png
03
在这里插入图片描述
04
在这里插入图片描述
05
在这里插入图片描述
06
在这里插入图片描述
07
在这里插入图片描述
08
在这里插入图片描述
说明

  1. jdk8以前, 新元素存入数组, 老元素挂在新元素下面
  2. jdk8以后, 新元素直接挂在老元素下面
  3. jdk8以后, 当链表的长度大于8, 且数组长度大于等于64, 当前链表就会自动转换为红黑树
  4. 第一步中的默认加载因子表示: 当数组中元素数量为 16 x 0.75 = 12 时, 数组就会扩容成原先的两倍

总结

在这里插入图片描述
在这里插入图片描述
note

  1. hashCodeequals 默认都是使用对象地址进行计算的, 所以, 对于自定义类, 必须重写 hashCodeequals 方法, 使用属性值进行比较和计算

02 HashSet 的三个问题

01 HashSet 为什么存取不一样
遍历 HashSet 时, 是从数组索引0开始遍历的, 遍历顺序不一定和添加元素顺序一致

02 HashSet 为什么没有索引
这是因为底层不仅有数组, 同一个数组位置, 是一个链表或一个红黑树, 同一个数组索引可能指向多个不同的数据

03 HashSet 去重机制
使用 hashCode equals 两个方法, 前者计算存储位置, 后者保证不会出现两个相同元素

10 LinkedHashSet

有序 不重复 无索引

  • 有序的原理: 底层数据结构依然是哈希表, 只是每个元素又额外的多了一个双链表的机制记录存储的顺序
  • 底层基于哈希表, 使用双链表记录添加顺序
    在这里插入图片描述

11 TreeSet

特点

  • 可排序 不重复 无索引
  • 可排序: 按照元素的默认规则 (从小到大) 排序
  • TreeSet 集合底层是基于红黑树的数据结构实现排序的, 增删改查性能都较好

代码示例

private static void testTreeSet() {
        // 创建集合
        TreeSet<Integer> ts = new TreeSet<>();

        // 添加元素
        ts.add(2);
        ts.add(3);
        ts.add(1);
        ts.add(7);
        ts.add(0);

        System.out.println(ts); // [0, 1, 2, 3, 7] 自动排序

        // 遍历
        Iterator<Integer> iterator = ts.iterator();

        while (iterator.hasNext()){
            int i = iterator.next();
            System.out.println("iterator: " + i);
        }
        /* 默认排序
        iterator: 0
        iterator: 1
        iterator: 2
        iterator: 3
        iterator: 7
         */

        for (int i : ts){
            System.out.println("for: " + i);
        }
        /* 默认排序
        for: 0
        for: 1
        for: 2
        for: 3
        for: 7
         */

        ts.forEach(integer -> System.out.println("Lambda: "+ integer));
        /* 默认排序
        Lambda: 0
        Lambda: 1
        Lambda: 2
        Lambda: 3
        Lambda: 7
         */
    }

排序规则

  1. 数值类型: Integer Double 按照从小到大的顺序进行排序
  2. 对于字符 字符串类型: 按照每个字符在 ASCII 码表中的数字升序进行排序
  3. 对于自定义对象: 有两种比较方式
    • 方式一: 默认排序/自然排序: javabean 类实现 Comparable 接口指定的比较规则
        public class Student implements Comparable<Student>{
      
        	public String name;
        	public int age;
      
            public Student(String name, int age){
                this.name = name;
                this.age = age;
            }
      
            @Override
            public int compareTo(Student o) {
                // 在该方法内指定排序的规则
                // 按年龄升序排序
                return this.age - o.age;
            }
        
            @Override
            public String toString() {
                return "Student{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        '}';
            }
        }
      
        private static void testTreeSetcompare() {
        
                Student s1 = new Student("zhangsan", 23);
                Student s2 = new Student("lisi", 24);
                Student s3 = new Student("wangwu", 26);
        
                TreeSet<Student> ts = new TreeSet<>();
        
                ts.add(s2);
                ts.add(s1);
                ts.add(s3);
        
                System.out.println(ts);
            }
        
        //[Student{name='zhangsan', age=23}, Student{name='lisi', age=24}, Student{name='wangwu', age=26}]
      
    • 图例分析在这里插入图片描述
    • 方式二: 比较器排序: 创建 TreeSet 对象时, 传递比较器 Comparator 指定规则. 使用原则: 默认使用第一种, 如果第一种不能满足当前需求, 就使用第二种
    •   /*
        对字符串进行排序, 首先按照长度排序, 一样长按照首字母依次排序
         */
        private static void testTreeSetComparator() {
            // String 已经实现了 Comparable 类, 重写了 compareTo 方法
            // 即其已经使用了第一种 TreeSet的排序方式
            // 当不满足要求时 我们可以使用TreeSet第二种排序方式
      
            /*
            1. 创建集合
            2. 使用有参构造, 重写Comparator的compare方法
                参数:
                    o1: 当前添加的元素
                    o2: 已经在红黑树中的元素
                    返回值: 负数 左边 0 舍弃 正数 右边
             */
            TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    // 按照长度进行排序
                    int i = o1.length() - o2.length();
      
                    i = i == 0 ? o1.compareTo(o2) : i; // 如果 i 为0, 就使用 String 在 TreeSet 集合中的默认排序规则
                    return i;
                }
            });
        }
      

总结
当创建的集合, 对对象如 String 两种排序方式都存在时, 优先使用第二种排序方式
在这里插入图片描述

12 使用场景分析

**加粗样式**

13 Map

Map 集合是一个键值对集合

业务场景: 购物车系统

特点

  • 每个元素是一个键值对, 键无序 不重复 无索引, 值可以重复, 默认值都为 null
  • Map 集合后面重复的键对应的值会覆盖前面重复键的值
  • 键和值可以是任意类型
  • Map 集合实现类特点
    • HashMap : 无序 不重复 无索引
    • LinkedHashMap : 有序 不重复 无索引
    • TreeMap: 排序 不重复 无索引

在这里插入图片描述

01 Map 集合体系特点

在这里插入图片描述

  • Map 集合实现类特点
    • HashMap : 无序 不重复 无索引
    • LinkedHashMap : 有序 不重复 无索引
    • TreeMap: 排序 不重复 无索引

02 Map 集合常用 API

Map 集合是双列集合的主接口, 所有的双列集合即键值对集合都会继承 Map 集合的功能

在这里插入图片描述
补充两个方法

  1. map.keySet() – 获取集合的全部键的值, 存储到一个 Set 集合中
  2. map.values() – 获取集合的全部值的值, 存储到一个 Collection 集合中
  3. map.putAll(Map) – 将参数中的集合的键值对添加到调用者的集合中, 重复的键会发生覆盖

03 Map 集合遍历

Lambda遍历方式
在这里插入图片描述
Map 集合遍历代码示例

// 遍历 map 集合 无序 不重复 无索引 hashmap linkednap 有序 treemap 排序
    public static void seeAllMap(){
        Map<String, String> map = new HashMap<>();

        map.put("zhangsan","qinghua");
        map.put("lisi","beida");
        map.put("wangwu","shangjiao");

        System.out.println("map: " + map);
        System.out.println("======");

        // 键遍历

        // 01 获取 map 集合的所有键
        Set<String> set = map.keySet();
        // 02 遍历 set 集合
        for (String key : set){
            String value = map.get(key);
            System.out.println(key + " ===>> " + value);
        }
        System.out.println("======");

        // 整体遍历 将 map 集合中的键值对作为一个 entry 对象, 获取所有 entry 对象到 set 集合中, 遍历 set 集合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String,String> entry : entries){
            System.out.print(entry + "\t");
        }

        // Lambda 遍历
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String s, String s2) {
                System.out.println(s + " => " + s2);
            }
        });

        map.forEach((k, v) -> System.out.println(k + " ==> " + v));
    }
// 综合案例: A B C D 四个景点, 有80个学生, 每个人只参观一个景点, 问每个景点有多少个人参观
    // 遍历 map 集合 无序 不重复 无索引 hashmap linkednap 有序 treemap 排序
    public static void visit(){
        int num = 80;
        // 模拟80个人的选择
        String[] place = {"A","B","C","D"};
        StringBuilder stringBuilder = new StringBuilder();

        Random r = new Random();
        for (int i = 0; i < num; i++) {
            stringBuilder.append(place[r.nextInt(place.length)]);
        }
        System.out.println(stringBuilder);

        // 将80个人的选择, 放到一个map集合中
        Map<Character,Integer> infos = new HashMap<>();
        for (int i = 0; i < num; i++) {
            Character c = stringBuilder.toString().charAt(i);
            if (!infos.containsKey(c)){
                infos.put(c,1);
            }else{
                infos.put(c,infos.get(c) + 1);
            }
        }

        System.out.println(infos);
    }

14 HashMap

底层同 HashSet 一样, 也是哈希表, 增删改查性能较好
依赖 hashCode equals 方法保证键的唯一, 对值不做要求, 如果键是自定义对象, 需要重写两个方法

在这里插入图片描述

LinkedHashMap

有序 不重复 无索引

底层也是哈希表, 使用一个双链表来记录添加元素的书必须, 保证存取有序

TreeMap

不重复 无索引 可排序

  • 可排序: 按照键数据的大小默认升序排序, 只对键排序
  • TreeMap 一定要排序, 可以使用默认排序, 或自定义排序

排序规则 – 针对键即可

  1. 类实现 Comparable 接口, 重写 compareTo 方法
  2. 集合创建时, 自定义比较器 Comparator 接口实现类对象, 重写 compare 方法

集合的嵌套

在这里插入图片描述
案例代码如下

private static void visitTwo() {
        /*
        某个班级多名学生, 班长提供4个景点(A,B,C,D), 每个人可选择多个景点
        统计选择人数最多的景点
         */
        // 每个学生的数据: map<String, List<String>>
        // 需要一个 map集合统计所有学生选择景点的数据
        int students = 10000;
        // 模拟学生数据
        String[] visit = {"A","B","C","D"};
        Random r = new Random();
        Set<String> visitList;
        Map<Integer, Set<String>> visitMap = new HashMap<>();
        int visitNum = 0;
        int visitPlace = 0;
        for (int i = 0; i < students; i++) {
            // 每个学生想参观景点数量 0 1 2 3 4
            visitNum = r.nextInt(visit.length + 1);
            // 每个学生都想参观哪几个景点
            visitList = new HashSet<>();
            for (int j = 0; j < visitNum; j++) {
                visitPlace = r.nextInt(visit.length);
                String place = visit[visitPlace];
                while (visitList.contains(place)){
                    visitPlace = r.nextInt(visit.length);
                    place = visit[visitPlace];
                }
                visitList.add(visit[visitPlace]);
            }
            // 将学生数据添加到 map 集合中
            visitMap.put(i+1,visitList);
        }

        Set<String> perVisitList = new HashSet<>();
        for (Integer i : visitMap.keySet()){
            perVisitList = visitMap.get(i);
            System.out.println("学生 " + i + "\t" + perVisitList);
        }

        // 统计学生数据
        Map<String,Integer> infos = new HashMap<>();
        for (Set<String> list : visitMap.values() ){
            for (String s : list) {
                if (infos.containsKey(s)){
                    infos.put(s,infos.get(s) + 1);
                }else {
                    infos.put(s,1);
                }
            }
        }

        for (String k : infos.keySet()){
            int v = infos.get(k);
            System.out.println("景点 " + k + " ===> " + v + " 人 ");
        }
    }

03 泛型

泛型:
是 jdk5 中引入的新特性, 可以在编译阶段约束操作的数据类型, 并进行检查, 只支持引用类型. 若没有指定集合的泛型, 集合默认元素的类型是 Object

格式: <数据类型>

优点:

  • 统一了数据类型
  • 把运行期间的问题提前到了编译期间, 避免了强制类型转换可能出现的异常, 因为在编译阶段类型就能确定下来

泛型的擦除
java 中泛型是伪泛型, 在编写的 java 文件中创建集合时, 带有泛型, 但是但编译成字节码文件后, 字节码文件中是没有泛型的, 称之为泛型的擦除. 在 java 底层中, 当创建集合添加元素时, 会检查是否为泛型类型, 然后将其放入集合中, 但是集合中的元素仍然是 Object 类型, 当获取元素时, java 会自动将元素强转为泛型类型

在这里插入图片描述

注意事项

  1. 泛型中不能写基本数据类型
  2. 指定泛型的具体类型后, 传递数据时, 可以传入该类类型或者其子类类型
  3. 如果不写泛型, 类型默认是 Object

01 泛型可以修饰的范围

使用场景: 当不确定变量的数据类型时, 可以使用泛型, 一般写作 <E>, 当然, 尖括号中可以使用任意字母

泛型可以修饰类 方法 接口

01 泛型类

public class MyArrayList<E> {
// 不确定类中会使用到什么数据类型, 那就加一个泛型吧

Object[] obj = new Object[10];
int size;

// 看这里不就用到泛型表示的数据类型来限制参数的数据类型了吗
public boolean add(E e){
	obj[size] = e;
	size++;
	return true;
}

// 看这里泛型还可以确定返回值类型哦
public E get(int index){
	return (E)obj[index];
}

@override
public String toString(){
	return Arrays.toString(obj);
}
  
}

02 泛型方法

  • 使用类名后面定义的泛型
  • 在方法声明上定义自己的泛型(当只有少数方法需要时, 推荐使用) – 在调用方法时确定泛型的具体类型

定义格式

修饰符 <T> 返回值类型 方法名(类型 变量名){

}// T 可以是任意字母, 这样方法中任一处就可以使用类型 T

public static <T> T get(T t){
        return t;
    }

03 泛型接口

定义格式

修饰符 interface 接口名<类型>{

}

使用如下

  1. 实现类给出泛型 public MyClass implements MyInterface<String>{}
  2. 实现类延续泛型, 创建对象时确定 public MyClass<E> implements MyInterface<E>{}
    在这里插入图片描述

04 泛型的继承和通配符

  • 泛型不具备继承性, 但是数据具备继承性
    + 如下图代码示例: 方法中形参使用泛型作为约束时, 实参只能是形参中泛型约束的类型, 即使实参是其子类也不行, 这就是泛型不具备继承性
    + 但是数据具备继承性, 即向集合中添加数据是可以添加泛型约束的类型的子类
    在这里插入图片描述

需求: 定义一个方法, 形参是一个集合, 但是集合中的数据类型不能确定
如上图的方法, 如何确定集合的泛型

利用泛型方法可以, 但是无法限制数据类型

所以使用泛型的通配符 ?

? -- 表示不确定的类型, 可以进行类型的限定

? extends E : 表示可以传递E或E的所有子类
? super E : 表示可以传递E或E的所有父类

public static void test(ArrayList<?> list){
	// ? 表示可以接收任意类型 使用 extends 或 super 进行范围限定 只写 ? 等同于 E
}

public static <E> void test(ArrayList<E> list){
	//
}

public static void test(ArrayList<? extends Fu> list){
	// 
}

04 不可变集合

  • 不可变集合, 就是不可修改, 添加, 删除元素的集合
  • 在集合创建的时候, 就赋值给集合相应的数据, 并且在整个生命周期中都不可改变
  • 应用
    • 数据不希望被修改, 或被不信任的库调用时, 修改数据

创建不可变集合的 API(jdk9之后)
在这里插入图片描述

05 Stream流

  • 在 java8 中, 得益于 Lambda 所带来的函数式编程, 引入了一个全新的 Stream 流概念
  • 目的: 用于简化集合和数组操作的 API
  • 但是无法改变原集合或数组中的数据

Stream 流思想的核心

  1. 先得到集合或数组的 Stream
  2. 利用 stream 提供的 API 操作元素

01 Stream流 的三类方法

  • 获取 Stream
    • 创建一条流水线, 并把数据放到流水线上进行操作
  • 中间方法
    • 流水线上的对数据的操作方法, 支持链式法则
  • 终结方法
    • 一个 Stream 流只能有一个终结方法, 是流水线上的最后一个操作

01 获取Stream
在这里插入图片描述
具体代码示例如下
调用相应的方法获取 Stream 流, Stream 流的泛型是由数组或集合的数据类型确定
在这里插入图片描述

02 中间操作方法
在这里插入图片描述

代码示例
在这里插入图片描述
在这里插入图片描述
03 终结方法
调用终结方法结束流 Stream
在这里插入图片描述

04 Stream 流的收集方法

  • 收集Stream流的含义: 就是把Stream流操作后的结果数据返回到集合或数组中去
    在这里插入图片描述
  • 代码示例 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

备注 – 还有很多方法哦, 去看 API 吧!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值