用框架思维学Java:集合概览

集合这个词,耳熟能详,从小学一年级开始,每天早上做操时都会听到这两个字:
在这里插入图片描述
高中数学又学习到了新的集合:
在这里插入图片描述
那么Java中的集合是什么呢?

一,前言

1,什么是Java集合

数学集合是Java集合的数学基础,所以有一致之处:

  • 都是由一组集在一起的对象构成
  • Java集合也可以应用数学集合的运算法则,如交集、并集、差集

但是,Java集合作为一个编程工具,更加突出了作为容器的作用:
在这里插入图片描述
就像一个果篮一样,Java容器具备存储、管理对象的功能,比如:

  • 存储水果
  • 向果篮中放入一个或者一批水果,动作是add、addAll、put、putAll
  • 把果篮中一个或者一批腐败的水果丢掉,动作是delete、remove
  • 从果篮中取出一个水果,动作是get
  • 判断果篮中是否包含苹果,动作是contains
  • 计算果篮中水果的个数,动作是size

Java集合是存储、管理对象的容器

2,Java集合的作用

Java集合到底有什么作用呢?这个问题值得好好思考一下,只有这样,才能更好的使用Java集合。

以开发一个简单的订单管理系统为例,我们需要存储并管理一系列订单信息,包括订单号、客户姓名、商品列表、总价等。

让我们通过对比使用Java集合不使用Java集合的情况,分析在实现该系统时可能遇到的困难。

使用Java集合的示例代码
import java.util.*;

class Product {
    String name;
    double price;

    Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

class Order {
    String orderId;
    String customerName;
    ArrayList<Product> productList;
    double totalAmount;

    Order(String orderId, String customerName) {
        this.orderId = orderId;
        this.customerName = customerName;
        this.productList = new ArrayList<>();
    }

    void addProduct(Product product) {
        // 不需要考虑集合容器的大小,容器会自动扩容
        productList.add(product);
        totalAmount += product.price;
    }
}

public class OrderManagement {
    public static void main(String[] args) {
        Order order = new Order("ORD123", "Alice");
        order.addProduct(new Product("Book", 19.99));
        order.addProduct(new Product("Pen", 4.99));
        
        System.out.println("Total Amount: " + order.totalAmount);
    }
}
不使用Java集合实现

如果Java集合框架不可用,我们需要手动实现数据结构来存储订单中的商品列表。这将涉及创建一个产品数组,并手动管理数组的大小调整、添加和删除产品等操作。

class OrderWithoutCollection {
    String orderId;
    String customerName;
    Product[] productList; // 假设产品数组
    int productCount;
    double totalAmount;

    OrderWithoutCollection(String orderId, String customerName) {
        this.orderId = orderId;
        this.customerName = customerName;
        this.productList = new Product[10]; // 初始化固定大小的数组
        this.productCount = 0;
    }

    boolean addProduct(Product product) {
        // 需要考虑数组的大小
        if (productCount == productList.length) {
            // 数组已满,需要手动扩展,这里简化处理,实际应复制数组并创建更大的数组
            System.out.println("Product list is full, cannot add more products.");
            return false;
        }
        productList[productCount++] = product;
        totalAmount += product.price;
        return true;
    }
    
    // 其他方法省略...
}

public class OrderManagementWithoutCollection {
    public static void main(String[] args) {
        OrderWithoutCollection order = new OrderWithoutCollection("ORD123", "Alice");
        order.addProduct(new Product("Book", 19.99));
        order.addProduct(new Product("Pen", 4.99));
        
        System.out.println("Total Amount: " + order.totalAmount);
    }
}
两种方式的差异分析
  • 复杂度增加:不使用集合框架,我们必须手动管理数组的大小,如上例中的OrderWithoutCollection类,当商品列表超出初始分配的空间时,需要手动扩展数组,这增加了代码的复杂度

  • 效率低下:手动数组管理缺乏动态调整大小的能力,可能导致空间浪费或频繁的数据复制(如果每次添加商品都动态扩展数组)。此外,查找和删除操作可能需要遍历整个数组,效率低下

  • 类型安全风险:没有泛型,我们可能会在操作数组时忘记检查类型,导致运行时错误

  • 功能缺失:没有现成的排序、过滤等操作,需要自己编写逻辑,如若想要按商品价格排序,需要实现复杂的排序算法

  • 代码可读性和维护性降低:代码中充斥着底层数据结构管理逻辑,而非业务逻辑,降低了代码的可读性和未来的可维护性

综上所述,没有Java集合框架的支持,开发任务将变得繁琐且低效,不仅影响开发进度,也可能影响最终产品的稳定性和性能。

二,集合概览

集合是Java中极其高频使用的工具类,从接口到实现,形成了一个庞杂的体系,要掌握这个庞杂的体系,要避免盲人摸象的学习方法,先看一看大象完整的照片,知大象有四条腿、一个长鼻子、两只眼睛、两根象牙和一个庞大的身躯,然后再靠近观察研究细节!

那么集合这只大象的样子是什么呢?

1,最简化的集合体系

在这里插入图片描述

集合分为两种:

  • 一种是单列集合,称之为Collection,如下图,集合每个位置的元素是一个单值

在这里插入图片描述

  • 一种是双列集合,称之为Map,如下图,集合每个位置上的元素是一个键值对,注意一对相关联的key、value合称一个元素
    在这里插入图片描述

2,完整的集合体系

在这里插入图片描述

Java集合框架分为两大类:CollectionMap

其中Collection又细分为ListSetQueue

  • List是有序的集合,即元素在List中的顺序和插入集合的先后顺序一致
  • List中的元素可以重复,比如把数字1先后多次插入List集合中,插入多少次就保存多少个
  • Set集合中的元素是无序的
  • Set集合不可以重复,比如把数字1先后多次插入Set集合中,后插入的会覆盖先插入的,实际效果相当于仅插入一次
  • Queue主要用于实现队列功能,遵循先进先出(FIFO)原则
继承体系简图
Collection
├── List
│   ├── ArrayList
│   ├── LinkedList
│   └── Vector
├── Set
│   ├── HashSet
│   ├── TreeSet
│   └── LinkedHashSet
└── Queue
    ├── PriorityQueue
    └── Deque
        ├── ArrayDeque
        └── LinkedList
Map
├── HashMap
├── TreeMap
└── LinkedHashMap

3,集合的主要操作

  • 增删查改add(E element)remove(Object o)contains(Object o)get(int index)/put(K key, V value)
  • 遍历:增强型for循环、迭代器(Iterator)、Lambda表达式等
  • 容量管理:如ArrayListensureCapacity(int minCapacity)trimToSize()

4,创建集合实现类示例

① LinkedList
import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("Apple");
        linkedList.add("Banana");
        linkedList.addFirst("Cherry");
        System.out.println(linkedList); // 输出: [Cherry, Apple, Banana]
        linkedList.removeFirst();
        System.out.println(linkedList); // 输出: [Apple, Banana]
    }
}

LinkedList基于双向链表实现,每个节点包含前驱和后继节点的引用,这使得插入和删除操作非常高效(O(1)),特别是在列表的开始和结束。然而,由于需要遍历链表来访问元素,随机访问操作(如通过索引访问)效率较低(O(n))。与ArrayList相比,LinkedList更适合频繁的插入和删除操作,但在随机访问方面不如ArrayList高效。

② ArrayList
import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add(1, "Cherry");
        System.out.println(arrayList); // 输出: [Apple, Cherry, Banana]
        arrayList.set(0, "Orange");
        System.out.println(arrayList); // 输出: [Orange, Cherry, Banana]
    }
}

ArrayList基于动态数组实现,内部维护了一个可自动增长的数组来存储元素。这意味着它提供了快速的随机访问(通过索引,O(1)),但插入和删除操作(特别是位于数组中间的操作)可能较慢(O(n)),因为可能需要移动后续元素。与LinkedList相比,它在索引访问和遍历顺序元素方面更高效,但在频繁插入和删除的场景下不如LinkedList灵活。

③ Vector
import java.util.Vector;

public class VectorDemo {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>(3);
        vector.add("Apple");
        vector.add("Banana");
        System.out.println(vector); // 输出: [Apple, Banana]
    }
}

Vector也是基于动态数组实现,与ArrayList相似,但其方法默认是线程安全的,通过同步机制确保了多线程环境下的安全访问。这增加了额外的性能开销,使得在单线程环境下,ArrayList通常比Vector更优。Vector在容量自动增长时,会增加当前容量的两倍,而ArrayList通常是1.5倍。一般情况下,不用Vector,因为其线程安全导致效率低。

④ HashSet
import java.util.HashSet;

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Apple"); // 重复元素,不会添加
        System.out.println(set); // 输出可能是 [Apple, Banana],顺序不确定
    }
}

: HashSet基于哈希表实现,使用hashCode()和equals()方法来确定元素的唯一性。它不保证元素的顺序,插入和查询操作平均时间复杂度为O(1),但最坏情况下(哈希冲突严重时)可能退化为O(n)。与TreeSet相比,它牺牲了排序特性,换取了更高的插入和查询性能。

⑤ TreeSet
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<String> set = new TreeSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        System.out.println(set); // 输出: [Apple, Banana, Cherry],自动排序
    }
}

TreeSet基于红黑树实现,因此它能够对元素进行自然排序(根据元素的Comparable实现)或定制排序(通过Comparator)。插入、删除和查找操作的平均时间复杂度为O(log n),并保证了元素的有序性。与HashSet相比,它提供了排序功能,但牺牲了在无序集合中可能获得的更快的平均性能。

⑥ LinkedHashSet
import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        LinkedHashSet<String> set = new LinkedHashSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        System.out.println(set); // 输出: [Apple, Banana, Cherry],保持插入顺序
    }
}

LinkedHashSetHashSet的一个子类,它同时维护了一个双向链表来保持元素的插入顺序。因此,它既有HashSet的快速查找特性(基于哈希表),又保持了元素的插入顺序,类似于LinkedHashMap。相比于普通的HashSet,它提供了更好的迭代性能(特别是当频繁迭代且需要保持顺序时),但消耗了额外的内存。

⑦ PriorityQueue
import java.util.PriorityQueue;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.add(5);
        queue.add(1);
        queue.add(3);
        System.out.println(queue.poll()); // 输出并移除最小元素: 1
    }
}

PriorityQueue是一个无界优先队列,它使用了堆数据结构(通常是二叉堆)来实现。这使得它能够高效地(O(log n))插入元素并返回队列中最小(或最大,取决于优先级比较器)的元素。与普通队列(FIFO)不同,PriorityQueue保证了队首元素始终是最优先的。它的实现不适合于随机访问,主要用于需要排序的插入和删除操作。

⑧ Deque
import java.util.ArrayDeque;

public class ArrayDequeDemo {
    public static void main(String[] args) {
        ArrayDeque<String> deque = new ArrayDeque<>();
        deque.addFirst("Apple");
        deque.addLast("Banana");
        System.out.println(deque.pop()); // 输出并移除头部元素: Apple
    }
}

ArrayDeque(双端队列)基于可调整大小的环形数组实现,提供了两端高效的插入和删除操作(O(1)),同时支持作为栈(push/pop)和队列(addFirst/removeFirst)使用。它在大多数场景下比传统的LinkedList表现更优,特别是在频繁的两端操作时,因为它减少了内存分配的开销并优化了缓存局部性。

⑨ HashMap
import java.util.HashMap;

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("Apple", 1);
        map.put("Banana", 2);
        System.out.println(map.get("Apple")); // 输出: 1
    }
}

HashMap基于哈希表实现,通过散列函数将键映射到数组的某个位置,以实现快速访问。它允许null键和null值,但不保证元素的顺序。插入、删除和查找操作的平均时间复杂度为O(1),在哈希冲突较少的情况下。与TreeMap相比,它放弃了排序功能,以换取更高的性能。

⑩ TreeMap
import java.util.TreeMap;

public class TreeMapDemo {
    public static void main(String[] args) {
        TreeMap<String, Integer> map = new TreeMap<>();
        map.put("Apple", 1);
        map.put("Banana", 2);
        map.put("Cherry", 3);
        System.out.println(map.keySet()); // 输出: [Apple, Banana, Cherry],自动排序
    }
}

TreeMap基于红黑树实现,保证了键的自然排序或定制排序。它提供了对键的有序访问,并且所有键值对都按照键的排序顺序排列。插入、删除和查找操作的平均时间复杂度为O(log n)。与HashMap相比,它牺牲了快速的无序访问,但提供了排序和区间访问的能力。

⑪ LinkedHashMap
import java.util.LinkedHashMap;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
        map.put("Apple", 1);
        map.put("Banana", 2);
        map.put("Cherry", 3);
        System.out.println(map.entrySet()); // 输出: 保持插入顺序
    }
}

LinkedHashMap继承自HashMap,并在其基础上增加了双向链表来维护元素的插入顺序或访问顺序(取决于构造参数)。这使得它既能提供快速的访问性能,又能维持元素的插入或访问顺序。与普通HashMap相比,它更适合那些需要按插入或访问顺序遍历键值对的场景,但会消耗更多内存。与TreeMap相比,它不提供键的自然排序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

模式识别涉及的常用算法

一、线性回归 1.算法执行流程&#xff1a; 算法的执行流程可以简述如下&#xff1a; 导入必要的库&#xff1a; 导入NumPy库&#xff0c;用于数值计算。导入Matplotlib库&#xff0c;用于数据可视化。导入Pandas库&#xff0c;用于数据处理&#xff08;尽管在这个例子中&#…

【Git】Git 的初识和安装

一、提出问题 不知道你工作或学习时&#xff0c;有没有遇到这样的情况&#xff1a;在编写各种文档时&#xff0c;为了防止文档丢失&#xff0c;更改失误&#xff0c;失误后能恢复到原来的版本&#xff0c;不得不复制出⼀个副本&#xff0c;比如&#xff1a; 设计文档v1设计文…

Python字符串操作详解(超详细)

Python字符串操作详解 目录 Python字符串操作详解一. 字符串创建二. 字符串拼接1. 使用 运算符2. 使用 .join() 方法 三. 字符串索引和切片1. 字符串索引2. 字符串切片3. 字符串长度和负索引4. 字符串不可变性 四. 字符串长度五. 字符串转换六. 查找子字符串七. 字符串替换八.…

xml创建模型组合体

XML创建模型组合体 创建步骤模型准备模型处理模型文件XML编写 效果 创建步骤 模型准备 CAD 提供的原始模型如下&#xff1a; 该模型存在的问题&#xff1a; 单位问题&#xff1a;CAD出图的是 mm 为单位&#xff0c;但是 mujoco 建模这边用的是以 m 为单位的&#xff1b;原点…

二刷算法训练营Day22 | 二叉树(8/9)

目录 详细布置&#xff1a; 1. 235. 二叉搜索树的最近公共祖先 2. 701. 二叉搜索树中的插入操作 3. 450. 删除二叉搜索树中的节点 详细布置&#xff1a; 1. 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共…

onenet踩坑连接mqtt

一定注意这个version为默认 完整说明https://open.iot.10086.cn/doc/v5/fuse/detail/922 注意这里的的device是名称&#xff0c;不是id,最好产品开发那里就是都是一个名字

华安保险:核心系统分布式升级,提升保费规模处理能力2-3倍 | OceanBase企业案例

在3月20日的2024 OceanBase数据库城市行的活动中&#xff0c;安保险信息科技部总经理王在平发表了以“保险行业核心业务系统分布式架构实践”为主题的演讲。本文为该演讲的精彩回顾。 早在2019年&#xff0c;华安保险便开始与OceanBase接触&#xff0c;并着手进行数据库的升级…

spring boot3登录开发-2(3邮件验证码接口实现)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 目录 写在前面 上文衔接 接口设计与实现 1.接口分析 2.实现思路 3.代码实现 1.定义验证码短信HTML模板枚举类 2.定义验证码业务接口 3. 验证码业务接口实现 4.控制层代码 4.测试 写…

三、Mapper XML的解析和注册使用

流程&#xff1a; 1.Resources加载MyBatis配置文件生成Reader字符流 2.SqlSessionFactoryBuilder开始引导构建SqlSessionFactory&#xff0c;包括两步&#xff1a; 第一步是在XMLConfigBuilder中使用dom4j解析xml文件&#xff0c;将解析的SQL包装成MappedStatement对象存入Con…

微信小程序-案例:本地生活-首页(不使用网络数据请求)

一、 1.页面效果&#xff1a; 二、 1.新建项目并添加页面 在app.json文件中&#xff1a; "pages": ["pages/home/home","pages/message/message","pages/contact/contact"] 2.配置导航栏效果 在app.json文件中&#xff1a; &quo…

Windows11系统 和Android 调试桥(Android Debug Bridge,ADB)工具安装,app抓取日志内容

文章目录 目录 文章目录 安装流程 小结 概要安装流程技术细节小结 概要 Android调试桥&#xff08;ADB&#xff09;是一种多功能命令行工具&#xff0c;它允许开发者与连接到计算机上的Android设备进行通信和控制。ADB工具的作用包括但不限于&#xff1a; 安装和卸载应用程序&…

Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解

前言 在Web开发中&#xff0c;Three.js是一个极为强大的库&#xff0c;它让开发者能够轻松地在浏览器中创建和展示3D图形。随着3D技术在网页设计、游戏开发、数据可视化等领域的广泛应用&#xff0c;用户与3D场景的交互变得日益重要。而要实现这种交互&#xff0c;一个核心的技…

初识C++ · 反向迭代器简介

目录 前言 反向迭代器的实现 前言 继模拟实现了list和vector之后&#xff0c;我们对迭代器的印象也是加深了许多&#xff0c;但是我们实现的都是正向迭代器&#xff0c;还没有实现反向迭代器&#xff0c;那么为什么迟迟不实现呢&#xff1f;因为难吗&#xff1f;实际上还好。…

点击重置按钮清除el-table排序状态的高亮样式

需求&#xff1a;需要点击按钮的时候&#xff0c;清除掉el-table排序状态的高亮样式 解决方法&#xff1a;table添加ref"tableData",然后使用this.$refs.tableData.clearSort()。 <el-table:data"tableData"style"width: 100%":header-cell-s…

PHP实现抖音小程序用户登录获取openid

目录 第一步、抖音小程序前端使用tt.login获取code 第二步、前端拿到code传给后端 第三步、方法1 后端获取用户信息 第四步、方法2 抖音小程序拿到用户信息把用户信息传给后端 code2Session抖音小程序用户登录后端文档 第一步、抖音小程序前端使用tt.login获取code 前端 …

如何解决chatgpt出现503 bad gateway的问题

昨日&#xff0c;ChatGPT官网挂了&#xff0c;也就是使用web网页端访问的用户&#xff0c;会出现 bad gateway 情况。我们去ChatGPT官方的监控查看&#xff0c;已经展示相关错误。 影响的范围有&#xff1a; 影响了 ChatGPT 所有计划的所有用户。影响包括所有与 ChatGPT 相关…

Ubuntu22.04之解决:emacs无法输入中文问题(二百四十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

js 选择一个音频文件,绘制音频的波形,从右向左逐渐前进。

选择一个音频文件&#xff0c;绘制波形&#xff0c;从右向左逐渐前进。 完整代码&#xff1a; <template><div><input type"file" change"handleFileChange" accept"audio/*" /><button click"stopPlayback" :…

实战:Zig 编写高性能 Web 服务(2)

1.1 编写 HTTP server 我们从python -m http.server 8000启动得到灵感&#xff0c;先确定好目标&#xff1a; 编写一个HTTP/1.1 http serverzig version 0.12.0 使用zig init搭建项目的前置工作你先自行搭建好&#xff0c;不会的翻看前面铺垫的章节熟悉zig的项目结构。 关键…

大型语言模型智能体(LLM Agent)在实际使用的五大问题

在这篇文章中&#xff0c;我将讨论人们在将代理系统投入生产过程中经常遇到的五个主要问题。我将尽量保持框架中立&#xff0c;尽管某些问题在特定框架中更加常见。 1. 可靠性问题 可靠性是所有代理系统面临的最大问题。很多公司对代理系统的复杂任务持谨慎态度&#xff0c;因…