【JavaSE学习】05-2常用API(集合)

JavaSE(B站黑马)学习笔记

01Java入门
02数组、方法
03面向对象&Java语法
04-1Java高级(Stream流、异常处理、日志技术)
04-2Java高级(文件处理-IO流)
04-3Java高级(多线程、网络编程)
04-4Java高级(单元测试、反射、注解、动态代理、XML)
05-1常用API
05-2常用API(集合)



前言

JavaSE(B站黑马)学习笔记 05-2常用API(集合)


05-2常用API(集合)

集合

集合概述

  • 集合是与数组类似,也是一种容器,用于装数据的

数组的特点

  • 数组定义完成并启动后,类型确定、长度固定
  • 不适合元素的个数和类型不确定的业务场景,更不适合做需要增删数据操作。
  • 数组的功能也比较的单一,处理数据的能力并不是很强大。

数组适合的场景

  • 当业务数据的个数是固定的,且都是同一批数据类型的时候,可以采取定义数组存储

集合的特点

集合是Java中存储对象数据的一种容器(不支持基本类型)

  • 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球。
  • 集合非常适合元素个数不能确定,且需要做元素的增删操作的场景。
  • 同时,集合提供的种类特别的丰富,功能也是非常强大的,开发中集合用的更多。
  • 注意:集合中只能存储引用类型数据,如果要存储基本类型数据可以选用包装类

集合适合的场景

  • 数据的个数不确定,需要进行增删元素的时候

集合类体系结构

  • Collection单列集合,每个元素(数据)只包含一个值。
  • Map双列集合,每个元素包含两个值(键值对)。
  • 注意:前期先掌握Collection集合体系的使用。

Collection集合

Collection集合的体系特点

Collection集合体系

Collection集合特点

  • List系列集合:添加的元素是有序、可重复、有索引。

    • ArrayList、LinekdList :有序、可重复、有索引。
  • Set系列集合:添加的元素是无序、不重复、无索引。

    • HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
    • TreeSet:按照大小默认升序排序、不重复、无索引。

集合对于泛型的支持

  • 集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型

Collection集合常用API

Collection集合

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

Collection API如下:

Collection集合的遍历方式

方式一:迭代器

迭代器遍历概述

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

it.hasNext()会先判断当前指向是否存在,存在true 反之

方式二:foreach(增强for循环)

增强for循环

  • 增强for循环:既可以遍历集合也可以遍历数组。
  • 它是JDK5之后出现的,其内部原理是一个iterator迭代器,遍历集合相当于迭代器的简化写法
  • 实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口

快捷键:输入首字母根据提示选择,下面的好一点

注意:修改第三方变量的值不会影响集合中的元素,因为它是独立出来的

方式三:lambda表达式

Lambda表达式详解:03面向对象 的Lambda表达式

Lambda表达式遍历集合

  • 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

Collection结合Lambda遍历的API

Collection集合存储自定义类型的对象


总结:集合中存储的是元素对象的地址。

List系列集合

Collection集合体系

List集合特点、特有API

List系列集合特点

  • ArrayList、LinekdList :有序,可重复,有索引。
  • 有序:存储和取出的元素顺序一致
  • 有索引:可以通过索引操作元素
  • 可重复:存储的元素可以重复

List集合特有方法

  • List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
方法名称说明
List subList(int fromIndex, int toIndex);截取集合返回一个子集(包前不包后)
int indexOf(Object o);返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1。

List集合的遍历方式小结

List集合的遍历方式

  1. 迭代器
  2. 增强for循环
  3. Lambda表达式
  4. for循环(因为List集合存在索引)

ArrayList集合的底层原理

  • ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。查询元素快,增删相对慢
  • 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。

List集合存储的元素要超过容量怎么办?

LinkedList集合的底层原理

LinkedList的特点

  • 底层数据结构是双链表,查询慢首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
  • 可以很方便实现栈和队列
  • LinkedList是List的实现类,其他Collection的功能和List的功能也都继承了。

LinkedList集合的特有功能

使用addFirst、removeFirst实现压栈、弹栈,是根据我们自己理解来写的,读代码的时候不友好。JDK1.6开始加上了push、pop代表压栈、弹栈,只是换个马甲,还是实现addFirst、removeFirst(逼格高)。入队也是用offerLast代替addLast

补充知识:集合的并发修改异常问题

问题引出

  • 当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。

哪些遍历存在问题?

  • 迭代器遍历集合且直接用集合删除元素的时候可能出现。
  • foreach(增强for循环)遍历集合且直接用集合删除元素的时候可能出现。
  • lambda表达式遍历集合且直接用集合删除元素的时候可能出现。


解决办法

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

Set系列集合

Set系列集系概述

Collection集合体系

Set系列集合特点

  • 无序:存取顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。

Set集合实现类特点

  • HashSet : 无序、不重复、无索引。
  • LinkedHashSet:有序、不重复、无索引。
  • TreeSet:排序、不重复、无索引。

Set集合的功能上基本上与Collection的API一致。

HashSet集合元素无序的底层原理:哈希表

HashSet底层原理

  • HashSet集合底层采取哈希表(散列表)存储的数据。
  • 哈希表是一种对于增删改查数据性能都较好的结构。

哈希表的组成

  • JDK8之前的,底层使用数组+链表组成
  • JDK8开始后,底层采用数组+链表+红黑树组成

在了解哈希表之前需要先理解哈希值的概念

哈希值

  • 是JDK根据对象的地址,按照某种规则算出来的int类型的数值。(可以理解成地址,但它不是地址)

Object类的API

  • public int hashCode():返回对象的哈希值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的。

其实就是散列表(哈希表)的除留余数法

HashSet集合元素去重复的底层原理


Set不是不重复的吗,为什么还有两个一模一样的student:因为HashSet是根据哈希表(散列表)用哈希值的除留余数法来实现无序、不重复、无索引的。如果两个不同的对象存储为同样的内容,它是没办法去重的,因为两个对象的地址不同所以哈希值不同。这时就必须重写对象的hashCode()和equals()方法

重写对象的hashCode()和equals()方法(自动生成即可)

重写equals()方法原理解释->05-1常用API 中的Object类:equals方法

重写hashCode()它会将一个个参数值传进去,如果内容一样就返回同样的哈希值

LinkedHashSet

LinkedHashSet集合概述和特点

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

TreeSet

TreeSet集合概述和特点

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

TreeSet集合默认的规则

  • 对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。
  • 对于自定义类型如Student对象,TreeSet无法直接排序。

结论:想要使用TreeSet存储自定义类型,需要制定排序规则

自定义类型没有指定排序规则允许就会报错

自定义排序规则

  • TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则

两种方式中,关于返回值的规则:

  • 如果认为第一个元素大于第二个元素返回正整数即可。
  • 如果认为第一个元素小于第二个元素返回负整数即可。
  • 如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。

方式一:

返回值规则跟Arrays类的排序方法很像,如果返回值相等它就会只保留一个元素,如下图,根据重量比较,红富士和黄苹果重量相等就只保留了红富士。

小技巧:保留相等的元素,通过判断只返回 1或-1

方式二:集合自带比较器对象进行规则定制

注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。

提示:可以用Lambda表达式简化

返回值规则跟Arrays类的排序方法很像,浮点型比较也使用Double。compare进行比较 参考->05-1常用API 中的Arrays类的排序方法

总结:Collection体系的特点、使用场景

无特殊业务基本都用ArrayList

补充知识:

可变参数

可变参数

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

可变参数的作用

  • 接收参数非常灵活,方便。可以不接收参数,可以接收1个或者多个参数,也可以接收一个数组
  • 可变参数在方法内部本质上就是一个数组

可变参数的注意事项:

  • 1.一个形参列表中可变参数只能有一个
  • 2.可变参数必须放在形参列表的最后面

集合工具类Collections

Collections集合工具类

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

Collections常用的API

Collections排序相关API

  • 使用范围:只能对于List集合的排序。

排序方式1:

默认首字符编码排序

排序方式2:

Comparator指定规则排序和上方TreeSet的一样

排序方式2:利用集合对不规则内容排序


Collection体系的综合案例:斗地主游戏

package com.gdit.d11_collection_Game;

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

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.组合点数和花色
        for (String size : sizes) {
            for (String color : colors) {
                // 6.封装成一个对象 (一个对象就代表一张牌)
                Card card = new Card(size, color);
                // 7.存入到集合容器中去
                allCards.add(card);
            }
        }
        // 8.另外将大小王存入到集合对象中去 "🤡" "🤶"   (大小王没点数)
        Card c1 = new Card("", "🤡");
        Card c2 = new Card("", "🤶");
        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> Tom = new ArrayList<>();
        List<Card> Tim = new ArrayList<>();
        List<Card> Jerry = new ArrayList<>();

        // 11.开始发牌(从牌集合中发出51张牌给三个玩家,剩余3张作为底牌)
        // addCards = [5♥, 7♦, A♦, 7♣, J♠, 🤶, ....
        //     i       0   1   2    3   4   5       % 3
        for (int i = 0; i < allCards.size()-3; i++) {
            // 先拿到当前牌对象
            Card c = allCards.get(i);
            if (i % 3 == 0){
                Tom.add(c); // Tom接牌
            }else if (i % 3 == 1){
                Tim.add(c); // Tim接牌
            }else if (i % 3 == 2){
                Jerry.add(c); // Jerry接牌
            }
        }

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

        // 13.给玩家的牌排好序(从大到小)
        // (拓展)


        // 14.输出玩家的牌
        System.out.println("Tom 的牌:" + Tom);
        System.out.println("Tim 的牌:" + Tim);
        System.out.println("Jerry 的牌:" + Jerry);
        System.out.println("底牌:" + lastThreeCards);
        
    }
}

(拓展)给玩家的牌排好序

添加一个字段index 给每个牌规定大小 如




package com.gdit.d11_collection_Game;

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

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 card = new Card(size, color, index);
                // 7.存入到集合容器中去
                allCards.add(card);
            }
        }
        // 8.另外将大小王存入到集合对象中去 "🤡" "🤶"   (大小王没点数)
        Card c2 = new Card("", "🤶", ++index); //小王
        Card c1 = 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> Tom = new ArrayList<>();
        List<Card> Tim = new ArrayList<>();
        List<Card> Jerry = new ArrayList<>();

        // 11.开始发牌(从牌集合中发出51张牌给三个玩家,剩余3张作为底牌)
        // addCards = [5♥, 7♦, A♦, 7♣, J♠, 🤶, ....
        //     i       0   1   2    3   4   5       % 3
        for (int i = 0; i < allCards.size()-3; i++) {
            // 先拿到当前牌对象
            Card c = allCards.get(i);
            if (i % 3 == 0){
                Tom.add(c); // Tom接牌
            }else if (i % 3 == 1){
                Tim.add(c); // Tim接牌
            }else if (i % 3 == 2){
                Jerry.add(c); // Jerry接牌
            }
        }

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

        // 13.给玩家的牌排好序(从大到小)
        sortCards(Tom);
        sortCards(Tim);
        sortCards(Jerry);

        // 14.输出玩家的牌
        System.out.println("Tom 的牌:" + Tom);
        System.out.println("Tim 的牌:" + Tim);
        System.out.println("Jerry 的牌:" + Jerry);
        System.out.println("底牌:" + lastThreeCards);

    }

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

Map集合

集合类体系结构

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

Map集合的概述

Map集合概述和使用

  • Map集合是一种双列集合,每个元素包含两个数据。
  • Map集合的每个元素的格式:key=value(键值对元素)。
  • Map集合也被称为“键值对集合”。

Map集合整体格式:

  • Collection集合的格式: [元素1,元素2,元素3…]
  • Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , …}

Map集合的使用场景之一:购物车系统

Map集合体系特点

Map集合体系

说明

  • 使用最多的Map集合是HashMap。
  • 重点掌握HashMap , LinkedHashMap , TreeMap。其他的后续理解。

Map集合体系特点

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

Map集合实现类特点

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

Map集合常用API

Map集合

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

Map API如下:

方法名称说明
Set keySet()获取全部键的集合
Collection values();获取全部值的集合

Map集合的遍历

Map集合的遍历方式有:3种。

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

方式一:键找值

遍历Map集合方式一:键找值流程(先提取出全部键,再通过键一个个找值)

  • 先获取Map集合的全部键的Set集合。
  • 遍历键的Set集合,然后通过键提取对应值。

键找值涉及到的API:

方式二:键值对

Map集合的遍历方式二:键值对

  • 先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。
  • 遍历Set集合,然后提取键以及提取值。

键值对涉及到的API:

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

​ 👇

使用foreach直接遍历map集合,发现Map集合的键值对元素直接是没有类型的,所以不可以直接foreach遍历集合。

如for(String s : maps) String那里是写String类型吗,可是值是Integer类型。反过来也不行

​ 👇

可以通过调用Map的方法 entrySet把Map集合转换成Set集合形式 把一个键值对当成一个类型:键值对类型(huawei=1000)

​ 👇

Set> entries = [(huawei=1000), (手表=10), (生活用品=10), (iphoneX=100), (娃娃=31)]

​ 👇

此时可以使用foreach遍历:for (Map.Entry entry : entries)

这种方式比较麻烦,建议使用方式一

方式三:lambda表达式

Map集合的遍历方式三:Lambda

  • 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。

Map结合Lambda遍历的API

流程

lambda表达式遍历源码其实也是用方式二的方法遍历的

案例

Map集合的实现类

HashMap

Map集合体系

HashMap的特点

  • HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引

  • 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。

  • HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。

  • 依赖hashCode方法和equals方法保证的唯一。

  • 如果要存储的是自定义对象,需要重写hashCode和equals方法。

  • 基于哈希表。增删改查的性能都较好。

实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。

LinkedHashMap

Map集合体系

LinkedHashMap集合概述和特点

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

TreeMap

TreeMap集合概述和特点

  • 由键决定特性:不重复、无索引、可排序
  • 可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
  • 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
  • TreeMap跟TreeSet一样底层原理是一样的。

TreeMap集合自定义排序规则有2种

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

集合的嵌套


创建不可变集合

什么是不可变集合?

  • 不可变集合,就是不可被修改的集合。
  • 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。

为什么要创建不可变集合?

  • 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
  • 或者当集合对象被不可信的库调用时,不可变形式是安全的。

如何创建不可变集合?

  • 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。(注:JDK9之后才有的方法
  • 这个集合不能添加,不能删除,不能修改。



注:

该内容是根据B站黑马程序员学习时所记,相关资料可在B站查询:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值