hashmap应用场景_工作中常用到的Java集合有哪些?应用场景是什么?

  • 秋招Java面试大纲:Java+并发+spring+数据库+Redis+JVM+Netty等
  • 疫情期间“闭关修炼”,吃透这本Java核心知识,跳槽面试不心慌
  • Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC

前言

Java集合是我认为在Java基础中最最重要的知识点了,Java集合是必须掌握的。我在实习/秋招面试的时候,只要是面到Java,那一定是少不了Java集合

作为一个新人,最关心的其实有一点:这个技术在工作中是怎么用的。换个说法:“工作中常用到的Java集合有哪些,应用场景是什么”

fd26c7df363b77745c8349bdbcd2aebd.png

List集合

List集合下最常见的集合类有两个:ArrayList和LinkedList

在工作中,我都是无脑用ArrayList。我问了两个同事:“你们在项目中用过LinkedList吗?”他们都表示没有。

众所周知,ArrayList底层是数组,LinkedList底层是链表。数组遍历速度快,LinkedList增删元素快。

为什么在工作中一般就用ArrayList,而不用LinkedList呢?原因也很简单:

  • 在工作中,遍历的需求比增删多,即便是增加元素往往也只是从尾部插入元素,而ArrayList在尾部插入元素也是O(1)
  • ArrayList增删没有想象中慢,ArrayList的增删底层调用的copyOf()被优化过,加上现代CPU对内存可以块操作,普通大小的ArrayList增删比LinkedList更快。

所以,在开发中,想到要用集合来装载元素,第一个想到的就是ArrayList。

那么来了,LinkedList用在什么地方呢?我们一般用在刷算法题上。把LinkedList当做一个先进先出的队列,LinkedList本身就实现了Queue接口

6a48ea4d20bc9dd72439004f318b5905.png

如果考虑线程安全的问题,可以看看CopyWriteOnArrayList,实际开发用得不多,但我觉得可以了解一下它的思想(CopyWriteOn),这个思想在Linux/文件系统都有用到。

bcb3a531c5e828ee50668f20552c103b.png

Set集合

Set集合下最常见的集合类有三个:HashSet、TreeSet、LinkedHashSet

List和Set都是集合,一般来说:如果我们需要保证集合的元素是唯一的,就应该想到用Set集合

比如说:现在要发送一批消息给用户,我们为了减少「一次发送重复的内容给用户」这样的错误,我们就用Set集合来保存用户的userId/phone

自然地,首先要保证最上游的那批用户的userId/phone是没有重复的,而我们用Set集合只是为了做一个兜底来尽可能避免重复发送的问题。

一般我们在开发中最多用到的也就是HashSet。TreeSet是可以排序的Set,一般我们需要有序,从数据库拉出来的数据就是有序的,可能往往写order by id desc比较多。而在开发中也很少管元素插入有序的问题,所以LinkedHashSet一般也用不上。

如果考虑线程安全的问题,可以考虑CopyOnWriteArraySet,用得就更少了(这是一个线程安全的Set,底层实际上就是CopyWriteOnArrayList)

TreeSet和LinkedHashSet更多的可能用在刷算法的时候。

22318a8e8117bc5d6d463237129eb736.png
8800684c0fe1856fc0d4cebfa92265ac.png

Map集合

Map集合最常见的子类也有三个:HashMap、LinkedHashMap、TreeMap

如果考虑线程安全问题,应该想到的是ConcurrentHashMap,当然了Hashtable也要有一定的了解,因为面试实在是问得太多太多了。

HashMap在实际开发中用得也非常多,只要是key-value结构的,一般我们就用HashMap。LinkedHashMap和TreeMap用的不多,原因跟HashSet和TreeSet一样。

ConcurrentHashMap在实际开发中也用得挺多,我们很多时候把ConcurrentHashMap用于本地缓存,不想每次都网络请求数据,在本地做本地缓存。监听数据的变化,如果数据有变动了,就把ConcurrentHashMap对应的值给更新了。

f40dad87f6d5401b6575769e45260d4c.png
a1fc7ad2d4145869f7bf9a072608e2e7.png

Queue队列

不知道大家有没有学过生产者和消费者模式,秋招面试的时候可能会让你手写一段这样的代码。最简单的方式就是用阻塞队列去写。类似下面:

生产者:

import java.util.Random;import java.util.Vector;import java.util.concurrent.atomic.AtomicInteger;public class Producer implements Runnable {    // true--->生产者一直执行,false--->停掉生产者    private volatile boolean isRunning = true;    // 公共资源    private final Vector sharedQueue;    // 公共资源的最大数量    private final int SIZE;    // 生产数据    private static AtomicInteger count = new AtomicInteger();    public Producer(Vector sharedQueue, int SIZE) {        this.sharedQueue = sharedQueue;        this.SIZE = SIZE;    }    @Override    public void run() {        int data;        Random r = new Random();        System.out.println("start producer id = " + Thread.currentThread().getId());        try {            while (isRunning) {                // 模拟延迟                Thread.sleep(r.nextInt(1000));                // 当队列满时阻塞等待                while (sharedQueue.size() == SIZE) {                    synchronized (sharedQueue) {                        System.out.println("Queue is full, producer " + Thread.currentThread().getId()                                + " is waiting, size:" + sharedQueue.size());                        sharedQueue.wait();                    }                }                // 队列不满时持续创造新元素                synchronized (sharedQueue) {                    // 生产数据                    data = count.incrementAndGet();                    sharedQueue.add(data);                    System.out.println("producer create data:" + data + ", size:" + sharedQueue.size());                    sharedQueue.notifyAll();                }            }        } catch (InterruptedException e) {            e.printStackTrace();            Thread.currentThread().interrupted();        }    }    public void stop() {        isRunning = false;    }}

消费者:

import java.util.Random;import java.util.Vector;public class Consumer implements Runnable {    // 公共资源    private final Vector sharedQueue;    public Consumer(Vector sharedQueue) {        this.sharedQueue = sharedQueue;    }    @Override    public void run() {        Random r = new Random();        System.out.println("start consumer id = " + Thread.currentThread().getId());        try {            while (true) {                // 模拟延迟                Thread.sleep(r.nextInt(1000));                // 当队列空时阻塞等待                while (sharedQueue.isEmpty()) {                    synchronized (sharedQueue) {                        System.out.println("Queue is empty, consumer " + Thread.currentThread().getId()                                + " is waiting, size:" + sharedQueue.size());                        sharedQueue.wait();                    }                }                // 队列不空时持续消费元素                synchronized (sharedQueue) {                    System.out.println("consumer consume data:" + sharedQueue.remove(0) + ", size:" + sharedQueue.size());                    sharedQueue.notifyAll();                }            }        } catch (InterruptedException e) {            e.printStackTrace();            Thread.currentThread().interrupt();        }    }}

Main方法测试:

import java.util.Vector;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test2 {    public static void main(String[] args) throws InterruptedException {        // 1.构建内存缓冲区        Vector sharedQueue = new Vector();        int size = 4;        // 2.建立线程池和线程        ExecutorService service = Executors.newCachedThreadPool();        Producer prodThread1 = new Producer(sharedQueue, size);        Producer prodThread2 = new Producer(sharedQueue, size);        Producer prodThread3 = new Producer(sharedQueue, size);        Consumer consThread1 = new Consumer(sharedQueue);        Consumer consThread2 = new Consumer(sharedQueue);        Consumer consThread3 = new Consumer(sharedQueue);        service.execute(prodThread1);        service.execute(prodThread2);        service.execute(prodThread3);        service.execute(consThread1);        service.execute(consThread2);        service.execute(consThread3);        // 3.睡一会儿然后尝试停止生产者(结束循环)        Thread.sleep(10 * 1000);        prodThread1.stop();        prodThread2.stop();        prodThread3.stop();        // 4.再睡一会儿关闭线程池        Thread.sleep(3000);        // 5.shutdown()等待任务执行完才中断线程(因为消费者一直在运行的,所以会发现程序无法结束)        service.shutdown();    }}

我的项目用阻塞队列也挺多的(我觉得跟个人编写的代码风格习惯有关),类似实现了上面的生产者和消费者模式。

真实场景例子:

  • 运营要发一条推送消息,首先需要去用户画像系统圈选一个人群,填写对应的人群ID和发送时间。
  • 我通过时间调度,通过RPC拿到人群的信息。遍历HDFS得到这个人群的每个userId
  • 将遍历的userId放到一个阻塞队列里边去,用多个线程while(true)取阻塞队列的数据

好处是什么?我在取userId的时候,会有个限制:要么超出了指定的时间,要么达到BatchSize的值。这样我就可以将相同内容的不同userId组成一个Task

本来100个userId是100个Task,现在我将100个userId放在一个Task里边(因为发送的内容是相同的,所以我可以这么干)。这样再往下游传的时候,并发量就降低了很多。

5d0d0ad0d24b67e401f7d4f154f43fb7.png

什么时候考虑线程安全

什么时候考虑线程安全的集合类,那当然是线程不安全的时候咯。那什么时候线程不安全?最常见的是:操作的对象是有状态的

虽然说,我们经常会听到线程不安全,但在业务开发中要我们程序员处理线程不安全的地方少之又少。比如说:你在写Servlet的时候,加过syn/lock锁吗?应该没有吧?

因为我们的操作的对象往往是无状态的。没有共享变量被多个线程访问,自然就没有线程安全问题了

621ced084467202fa52f952e0730447e.png

SpringMVC是单例的,但SpringMVC都是在方法内操作数据的,每个线程进入方法都会生成栈帧,每个栈帧的数据都是线程独有的,如果不设定共享变量,不会有线程安全问题。

上面只是简单举了SpringMVC的例子(只是为了更好的理解);

一句话总结:只要涉及到多个线程操作一个共享变量的时候,就要考虑是不是要用线程安全的集合类

c13747c0218f663e96f1a36c9cae7425.png

最后

还是想强调一下,Java集合虽然在工作中不是每个都经常用得到,但是还是得重点学习学习。

如果你学习到了源码,可能你在创建集合的时候就会指定了集合的大小(即便我们知道它能动态扩容)

如果你想要去面试,Java集合是肯定少不了的,必问的一个知识点,你学会了就是送分题

现在已经工作有一段时间了,为什么还来写Java集合呢,原因有以下几个:

  • 我是一个对排版有追求的人,如果早期关注我的同学可能会发现,我的GitHub、文章导航的read.me会经常更换。现在的GitHub导航也不合我心意了(太长了),并且早期的文章,说实话排版也不太行,我决定重新搞一波。
  • 我的文章会分发好几个平台,但文章发完了可能就没人看了,并且图床很可能因为平台的防盗链就挂掉了。又因为有很多的读者问我:”你能不能把你的文章转成PDF啊?“
  • 我写过很多系列级的文章,这些文章就几乎不会有太大的改动了,就非常适合把它们给”持久化“。


作者:Java3y
原文链接:https://juejin.im/post/5e7c05236fb9a009a6764ef9

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zookeeper入门总结

什么是Zookeeper 官方定义: zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管…

rabbitmq启动失败-报Failed to load advanced configuration file解决方法

【README】 1、本文基于 centos8 搭建 rabbitmq; 版本信息如下: rabbitmq rabbitmq-server-3.8.12-1.el8.noarch.rpm erlang erlang 23.2.5 (erts-11.1.8) 上述两者版本一定要强对应,否则mq启动失败,参见官网 https://rabb…

Java抽象类与接口的区别

转载自 Java抽象类与接口的区别 抽象类 抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板。以JDK中的GenericServlet为例: public abstract class GenericServlet implements Servlet…

gateway坑点:gateway有Controller时会直接处理对应的路径并返回

前言 使用gateway过程中,因为在gateway中加入了一个标注RestController的类,导致请求直接由gateway中的RestController的类处理并返回。花费了几小时才发现问题原因,特此记录 问题 以gateway作为网关转发请求得到的响应,所有字…

电脑任务栏跑到右边去了_电脑没有声音怎么解决 电脑没有声音解决方法【详解】...

电脑没有声音怎么办? 这个话题在我耳边已经听到过了无数次了,处理故障也是由以前的摸索解决到现在可以快速的找到原因。但对于新手朋友来说可能不知道从哪里入手,对于电脑没有声音怎么办,也给不出一个对策,今天围绕这个问题&…

rabbitmq-java生产者消费者

【README】 本文使用java 连接rabbitmq&#xff0c;模拟生产者&#xff0c;消费者场景 【1】项目搭建 1&#xff09;maven项目&#xff0c;依赖 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance…

Java 8新特性——default方法(defender方法)介绍

转载自 Java 8新特性——default方法&#xff08;defender方法&#xff09;介绍 我们都知道在Java语言的接口中只能定义方法名&#xff0c;而不能包含方法的具体实现代码。接口中定义的方法必须在接口的非抽象子类中实现。下面就是关于接口的一个例子&#xff1a; 1 2 3 4 5 6 …

对于高并发的理解及实践方案

目录01 如何理解高并发&#xff1f;02 高并发系统设计的目标是什么&#xff1f;2.1标题宏观目标高并发绝不意味着只追求高性能&#xff0c;这是很多人片面的理解。2.2 微观目标2.2.1 性能指标2.2.2 可用性指标2.2.3 可扩展性指标03 高并发的实践方案有哪些&#xff1f;3.1 通用…

ubuntu ifconfig_Ubuntu 设置固定 IP 最简单的方法!

因为鼠标共享软件需要使用网络连接&#xff0c;台式机的 IP 总是变化导致笔记本每次重启都要重新配置 IP&#xff0c;所以我给 ubuntu 设置了固定 IP&#xff0c;方法很简单&#xff0c;直接再系统设置里面配置就可以了。1. 查看默认网络配置打开系统「Setting」&#xff0c;打…

rabbitmq-发布订阅模式

【README】 本文po出 mq的发布订阅模式&#xff0c;及代码示例&#xff1b; 【1】intro 1&#xff09; 角色&#xff1a; 有4个角色&#xff0c; 包括 生产者&#xff0c;消费者&#xff0c; 交换机 exchange&#xff08;X&#xff09;&#xff0c; 队列&#xff1b; 2&#…

深入Java类型信息:RTTI和反射

转载自 「深入Java」类型信息&#xff1a;RTTI和反射 1.RTTI Run-Time Type Infomation 运行时类型信息 为什么需要RTTI&#xff1f; 越是优秀的面向对象设计&#xff0c;越是强调高内聚低耦合&#xff0c;正如依赖倒转原则所说&#xff1a;“无论是高层模块还是低层模块&#…

设计费收费标准2002修订_@设计师,2019景观园林设计收费标准,注意查收!

From&#xff1a;景观邦(ID&#xff1a;LA_bang)一、设计费量的定制&#xff1a;设计费在10万以上的(包括10万)省外工程方能承接。其工作内容有:景观概念规划设计、方案设计、初步设计、施工图设计、效果图绘制及多媒体制作等。二、设计费的取费标准&#xff1a;居住区、道路、…

rabbitmq-路由模式-routingkey

【README】 本文po出 rabbitmq路由模式&#xff1b; 【1】intro to 路由模式 特点1&#xff09;队列与交换机的绑定&#xff0c;不能是任意绑定&#xff0c; 而是指定一个路由key-routingkey&#xff1b; 特点2&#xff09;消息的发送方向在向 exchange-交换机发送消息时&…

一文理清HashMap的实现及细节

前言 最近阅读了许多HashMap实现及源码分析的文章&#xff0c;特意此文记录HashMap的知识点。 HashMap 底层由 数组 链表 组成&#xff0c;在 jdk1.7 和 1.8 中具体略有不同。 JDK1.7的HashMap 数据结构&#xff1a;图片来源 核心成员变量 图片来源 初始化桶大小&…

java提高篇之详解内部类

转载自 java提高篇之详解内部类内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类&#xff0c;对内部类也只是略知一二)。第一次见面内部类我们从外面看是非常容易理解的&#xff0c;无非就是在一个类的内部在定义一个类。123456789101112…

rabbitmq-通配符模式

【README】 本文介绍 通配符模式&#xff0c;及代码示例 【1】intro to rabbitmq通配符模式 0&#xff09;通配符模式-交换机类型为 Topic&#xff1b; 1&#xff09;与路由模式相比&#xff0c;相同点是 两者都可以通过 routingkey 把消息转发到不同的队列&#xff1b; 不同…

中海达手部链接电脑安装软件_山东水文局:较大含沙量条件下中海达ADCP外接测深仪测流系统试验成功...

近日&#xff0c;黄委山东水文水资源局与中海达海洋公司联合&#xff0c;在黄河泺口水文站较大含沙量环境下进行了ADCP外接测深仪、GNSS测流试验&#xff0c;试验取得了圆满成功。泺口水文站简介1泺口水文站概况泺口水文站位于山东省济南市天桥区黄河泺口浮桥南岸&#xff0c;隶…

一文搞懂ThreadLocal及相关的内存泄露问题

首先&#xff0c;看一张整体的结构图&#xff0c;来帮助理解 什么是ThreadLocal ThreadLocal用于创建线程局部变量&#xff0c;如果创建一个ThreadLocal变量&#xff0c;那么访问这个变量的每个线程都会有这个变量的一个副本&#xff0c;在实际多线程操作的时候&#xff0c;…

resnet50加入fpn_FPN+SSD同时兼顾速度和精度的检测器(二)

本文首发于知乎专栏“人工智能从入门到逆天杀神”&#xff0c;本文以及本专栏所有算法源代码都可以在神力AI平台获取&#xff0c;如果你没有GPU但需要预训练模型或者你想获取更多开箱即用的AI算法&#xff0c;欢迎加入我们的会员&#xff0c;一杯咖啡即可带你入门AI&#xff0c…

Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单(转)

转自&#xff1a; https://juejin.cn/post/6844903903130042376 文末有源代码&#xff0c;非常棒 摘要&#xff1a; 本篇博文是“Java秒杀系统实战系列文章”的第十篇&#xff0c;本篇博文我们将采用RabbitMQ的死信队列的方式处理“用户秒杀成功生成订单后&#xff0c;却迟…