427
README.md
@ -1,18 +1,15 @@
|
||||
> 关于 JavaGuide 的相关介绍请看:[《从编程小白到做了一个接近 90k 点赞的一个国产 Java 开源项目》](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok)
|
||||
👍《JavaGuide 面试突击版》PDF 版本+3 本 PDF Java 学习手册,在公众号 **[JavaGuide](#公众号)** 后台回复“**面试突击**”即可获取。
|
||||
|
||||
👍 图解操作系统+HTTP+计算机网络的 PDF 资料[点此链接即可下载](https://cowtransfer.com/s/fbed14f0c22a4d)。
|
||||
|
||||
> 一些闲话:
|
||||
>
|
||||
> 准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide) ,欢迎加入[我的星球](https://wx.zsxq.com/dweb2/index/group/48418884588288)获取更多实用干货。
|
||||
>
|
||||
> 阿里云最近在做活动,服务器不到 10 元/月,小伙伴们搭建一个网站提高简历质量。支持国内开源做的比较好的公司
|
||||
>
|
||||
> 项目的发展离不开你的支持,如果 JavaGuide 帮助到了你找到自己满意的 offer,那就[请作者喝杯咖啡吧](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok)☕!我会继续将项目完善下去!加油!
|
||||
|
||||
如果 Github 访问速度比较慢或者图片无法刷新出来的话,可以转移到[码云](https://gitee.com/SnailClimb/JavaGuide)查看,或者[在线阅读](https://snailclimb.gitee.io/javaguide)。**如果你要提交 issue 或者 pr 的话请到 [Github](https://github.com/Snailclimb/JavaGuide) 提交。**
|
||||
|
||||
《JavaGuide 面试突击版》PDF 版本+3 本 PDF Java 学习手册,在公众号 **[JavaGuide](#公众号)** 后台回复“**面试突击**”即可获取。
|
||||
|
||||
如要进群或者请教问题,请[联系我](#联系我) (备注来自 Github。请直入问题,工作时间不回复)。
|
||||
|
||||
**开始阅读之前必看** :[完结撒花!JavaGuide 面试突击版来啦!](./docs/javaguide面试突击版.md) 。
|
||||
> 1. **JavaGuide 介绍**:关于 JavaGuide 的相关介绍请看:[关于 JavaGuide 的一些说明](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok) 。PDF 版本请看:[完结撒花!JavaGuide 面试突击版来啦!](./docs/javaguide面试突击版.md) 。
|
||||
> 2. **在线阅读** :如果 Github 访问速度比较慢或者图片无法刷新出来的话,可以转移到[码云](https://gitee.com/SnailClimb/JavaGuide)查看或者[在线阅读](https://snailclimb.gitee.io/javaguide)。如果你要提交 issue 或者 pr 的话建议到 [Github](https://github.com/Snailclimb/JavaGuide) 提交。
|
||||
> 3. **面试专版** :准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide) ,欢迎加入[我的星球](https://wx.zsxq.com/dweb2/index/group/48418884588288)获取更多实用干货。
|
||||
> 4. **阿里云活动** :阿里云最近在做活动,服务器不到 10 元/月,小伙伴们搭建一个网站提高简历质量。支持国内开源做的比较好的公司
|
||||
> 5. **联系我** :如要进群或者请教问题,请[联系我](#联系我) (备注来自 Github。请直入问题,工作时间不回复)。
|
||||
> 6. **转载须知** :以下所有文章如非文首说明皆为我(Guide哥)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!⛽️
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
|
||||
@ -33,36 +30,31 @@
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://mp.weixin.qq.com/s/li9_YXNVxan6Qgt3Q9FYqA">
|
||||
<img src="./media/sponsor/wangyi.png" style="margin: 0 auto;width:450px" /></a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://t.1yb.co/5p8J">
|
||||
<img src="./media/sponsor/xiangxue.png" style="margin: 0 auto;width:450px" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://w.url.cn/s/AS6JeXA">
|
||||
<img src="./media/sponsor/kaikeba.png" style="margin: 0 auto;width:450px" /></a>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## 目录
|
||||
|
||||
- [目录](#目录)
|
||||
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
|
||||
|
||||
<!-- code_chunk_output -->
|
||||
|
||||
- [Java](#java)
|
||||
- [基础](#基础)
|
||||
- [容器](#容器)
|
||||
- [并发](#并发)
|
||||
- [JVM](#jvm)
|
||||
- [其他](#其他)
|
||||
- [JVM (必看 :+1:)](#jvm-必看-1)
|
||||
- [新特性](#新特性)
|
||||
- [网络](#网络)
|
||||
- [操作系统](#操作系统)
|
||||
- [Linux](#linux)
|
||||
- [数据结构与算法](#数据结构与算法)
|
||||
- [数据结构](#数据结构)
|
||||
- [算法](#算法)
|
||||
@ -70,90 +62,89 @@
|
||||
- [MySQL](#mysql)
|
||||
- [Redis](#redis)
|
||||
- [系统设计](#系统设计)
|
||||
- [必知](#必知)
|
||||
- [编码之道(必看 :+1:)](#编码之道必看-1)
|
||||
- [常用框架](#常用框架)
|
||||
- [Spring/SpringBoot](#springspringboot)
|
||||
- [MyBatis](#mybatis)
|
||||
- [Netty](#netty)
|
||||
- [Netty (必看 :+1:)](#netty-必看-1)
|
||||
- [认证授权](#认证授权)
|
||||
- [JWT](#jwt)
|
||||
- [SSO(单点登录)](#sso单点登录)
|
||||
- [分布式](#分布式)
|
||||
- [分布式搜索引擎](#分布式搜索引擎)
|
||||
- [搜索引擎](#搜索引擎)
|
||||
- [RPC](#rpc)
|
||||
- [消息队列](#消息队列)
|
||||
- [API 网关](#api-网关)
|
||||
- [分布式 id](#分布式id)
|
||||
- [分布式限流](#分布式限流)
|
||||
- [分布式接口幂等性](#分布式接口幂等性)
|
||||
- [分布式 id](#分布式-id)
|
||||
- [ZooKeeper](#zookeeper)
|
||||
- [其他](#其他-1)
|
||||
- [数据库扩展](#数据库扩展)
|
||||
- [大型网站架构](#大型网站架构)
|
||||
- [性能测试](#性能测试)
|
||||
- [高并发](#高并发)
|
||||
- [高可用](#高可用)
|
||||
- [微服务](#微服务)
|
||||
- [Spring Cloud](#spring-cloud)
|
||||
- [必会工具](#必会工具)
|
||||
- [Git](#git)
|
||||
- [Docker](#docker)
|
||||
- [其他](#其他-2)
|
||||
- [高并发](#高并发)
|
||||
- [消息队列](#消息队列)
|
||||
- [读写分离](#读写分离)
|
||||
- [分库分表](#分库分表)
|
||||
- [负载均衡](#负载均衡)
|
||||
- [高可用](#高可用)
|
||||
- [CAP 理论](#cap-理论)
|
||||
- [BASE 理论](#base-理论)
|
||||
- [限流](#限流)
|
||||
- [降级](#降级)
|
||||
- [熔断](#熔断)
|
||||
- [排队](#排队)
|
||||
- [大型网站架构](#大型网站架构)
|
||||
- [工具](#工具)
|
||||
- [面试指南](#面试指南)
|
||||
- [Java 学习常见问题汇总](#java学习常见问题汇总)
|
||||
- [资源](#资源)
|
||||
- [Java 程序员必备书单](#java程序员必备书单)
|
||||
- [实战项目推荐](#实战项目推荐)
|
||||
- [Github](#github)
|
||||
- [待办](#待办)
|
||||
- [说明](#说明)
|
||||
- [Java 学习常见问题汇总](#java-学习常见问题汇总)
|
||||
- [书单](#书单)
|
||||
- [其他](#其他)
|
||||
- [待办](#待办)
|
||||
- [联系我](#联系我)
|
||||
- [捐赠支持](#捐赠支持)
|
||||
- [Contributor](#contributor)
|
||||
- [公众号](#公众号)
|
||||
|
||||
<!-- /code_chunk_output -->
|
||||
|
||||
|
||||
## Java
|
||||
|
||||
### 基础
|
||||
|
||||
**基础知识系统总结:**
|
||||
**知识点/面试题:**(必看:+1: )
|
||||
|
||||
1. **[Java 基础知识](docs/java/Java基础知识.md)**
|
||||
2. **[Java 基础知识疑难点/易错点](docs/java/Java疑难点.md)**
|
||||
3. [【选看】J2EE 基础知识](docs/java/J2EE基础知识.md)
|
||||
1. **[Java 基础知识](docs/java/basis/Java基础知识.md)**
|
||||
2. **[Java 基础知识疑难点/易错点](docs/java/basis/Java基础知识疑难点.md)**
|
||||
|
||||
**重要知识点详解:**
|
||||
|
||||
1. [枚举](docs/java/basic/用好Java中的枚举真的没有那么简单.md) (很重要的一个数据结构,用好枚举真的没有那么简单!)
|
||||
2. [Java 常见关键字总结:final、static、this、super!](docs/java/basic/final,static,this,super.md)
|
||||
3. [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basic/reflection.md)
|
||||
4. [代理模式详解:静态代理+JDK/CGLIB 动态代理实战(动态代理和静态代理的区别?JDK 动态代理 和 CGLIB 动态代理的区别?)](docs/java/basic/java-proxy.md)
|
||||
|
||||
**其他:**
|
||||
|
||||
1. [JAD 反编译](docs/java/JAD反编译tricks.md)
|
||||
2. [手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md)
|
||||
1. [枚举](docs/java/basis/用好Java中的枚举真的没有那么简单.md) (很重要的一个数据结构,用好枚举真的没有那么简单!)
|
||||
2. [Java 常见关键字总结:final、static、this、super!](docs/java/basis/Java常见关键字总结:final,static,this,super.md)
|
||||
3. [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basis/什么是反射机制?反射机制的应用场景有哪些?.md)
|
||||
4. [代理模式详解:静态代理+JDK/CGLIB 动态代理实战](docs/java/basis/静态代理+JDK,CGLIB动态代理实战.md)
|
||||
5. [BIO,NIO,AIO 总结 ](docs/java/basis/BIO,NIO,AIO总结.md)
|
||||
|
||||
### 容器
|
||||
|
||||
1. **[Java 容器常见面试题/知识点总结](docs/java/collection/Java集合框架常见面试题.md)**
|
||||
2. 源码分析:[ArrayList 源码](docs/java/collection/ArrayList.md) 、[LinkedList 源码](docs/java/collection/LinkedList.md) 、[HashMap(JDK1.8)源码](docs/java/collection/HashMap.md) 、[ConcurrentHashMap 源码](docs/java/collection/ConcurrentHashMap.md)
|
||||
1. **[Java 容器常见面试题/知识点总结](docs/java/collection/Java集合框架常见面试题.md)** (必看 :+1:)
|
||||
2. **源码分析** :[ArrayList 源码+扩容机制分析](docs/java/collection/ArrayList源码+扩容机制分析.md) 、[LinkedList 源码](docs/java/collection/LinkedList源码分析.md) 、[HashMap(JDK1.8)源码+底层数据结构分析](<docs/java/collection/HashMap(JDK1.8)源码+底层数据结构分析.md>) 、[ConcurrentHashMap 源码+底层数据结构分析](docs/java/collection/ConcurrentHashMap源码+底层数据结构分析.md)
|
||||
|
||||
### 并发
|
||||
|
||||
**[多线程学习指南](./docs/java/Multithread/多线程学习指南.md)**
|
||||
并发这部分内容非常重要,还是面试中的重点中的重点!但是,学习起来难度较大,因此我写了:**[多线程学习指南](./docs/java/multi-thread/多线程学习指南.md)** 帮助你学习。
|
||||
|
||||
**面试题总结:**
|
||||
**知识点/面试题:** (必看 :+1:)
|
||||
|
||||
1. **[Java 并发基础常见面试题总结](docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md)**
|
||||
2. **[Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)**
|
||||
1. **[Java 并发基础常见面试题总结](docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md)**
|
||||
2. **[Java 并发进阶常见面试题总结](docs/java/multi-thread/2020最新Java并发进阶常见面试题总结.md)**
|
||||
|
||||
**面试常问知识点:**
|
||||
**重要知识点详解:**
|
||||
|
||||
1. [并发容器总结](docs/java/Multithread/并发容器总结.md)
|
||||
2. **线程池**:[Java 线程池学习总结](./docs/java/Multithread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/Multithread/best-practice-of-threadpool.md)
|
||||
2. **线程池**:[Java 线程池学习总结](./docs/java/multi-thread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/multi-thread/拿来即用的线程池最佳实践.md)
|
||||
3. [乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md)
|
||||
4. [万字图文深度解析 ThreadLocal](docs/java/Multithread/ThreadLocal.md)
|
||||
5. [JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md)
|
||||
6. [AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md)
|
||||
4. [ ThreadLocal 关键字解析](docs/java/multi-thread/万字详解ThreadLocal关键字.md)
|
||||
5. [并发容器总结](docs/java/multi-thread/并发容器总结.md)
|
||||
6. [JUC 中的 Atomic 原子类总结](docs/java/multi-thread/Atomic原子类总结.md)
|
||||
7. [AQS 原理以及 AQS 同步组件总结](docs/java/multi-thread/AQS原理以及AQS同步组件总结.md)
|
||||
|
||||
### JVM
|
||||
### JVM (必看 :+1:)
|
||||
|
||||
1. **[Java 内存区域](docs/java/jvm/Java内存区域.md)**
|
||||
2. **[JVM 垃圾回收](docs/java/jvm/JVM垃圾回收.md)**
|
||||
@ -165,48 +156,38 @@
|
||||
8. [JVM 配置常用参数和常用 GC 调优策略](docs/java/jvm/GC调优参数.md)
|
||||
9. **[【加餐】大白话带你认识 JVM](docs/java/jvm/[加餐]大白话带你认识JVM.md)**
|
||||
|
||||
### 其他
|
||||
### 新特性
|
||||
|
||||
1. **Linux IO** : [Linux IO](docs/java/Linux_IO.md)
|
||||
2. **I/O** :[BIO,NIO,AIO 总结 ](docs/java/BIO-NIO-AIO.md)
|
||||
3. **Java 8** :[Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md)、[Java 8 学习资源推荐](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/What's%20New%20in%20JDK8/Java8foreach指南.md)
|
||||
4. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/jdk-new-features/new-features-from-jdk8-to-jdk14.md)
|
||||
5. Java 编程规范:**[Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md)** 、[告别编码 5 分钟,命名 2 小时!史上最全的 Java 命名规范参考!](docs/java/java-naming-conventions.md)
|
||||
6. 设计模式 :[设计模式系列文章](docs/system-design/设计模式.md)
|
||||
1. **Java 8** :[Java 8 新特性总结](docs/java/new-features/Java8新特性总结.md)、[Java 8 学习资源推荐](docs/java/new-features/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/new-features/Java8foreach指南.md)
|
||||
2. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md)
|
||||
|
||||
## 网络
|
||||
|
||||
1. [计算机网络常见面试题](docs/network/计算机网络.md)
|
||||
2. [计算机网络基础知识总结](docs/network/干货:计算机网络知识总结.md)
|
||||
2. [计算机网络基础知识总结](docs/network/计算机网络知识总结.md)
|
||||
|
||||
## 操作系统
|
||||
|
||||
[最硬核的操作系统常见问题总结!](docs/operating-system/basis.md)
|
||||
|
||||
### Linux
|
||||
|
||||
- [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md)
|
||||
- [Shell 编程入门](docs/operating-system/Shell.md)
|
||||
- [我为什么从 Windows 转到 Linux?](docs/operating-system/完全使用GNU_Linux学习.md)
|
||||
- [Linux IO 模型](docs/operating-system/Linux_IO.md)
|
||||
- [Linux 性能分析工具合集](docs/operating-system/Linux性能分析工具合集.md)
|
||||
1. [操作系统常见问题总结!](docs/operating-system/basis.md)
|
||||
2. [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md)
|
||||
3. [Shell 编程入门](docs/operating-system/Shell.md)
|
||||
|
||||
## 数据结构与算法
|
||||
|
||||
### 数据结构
|
||||
|
||||
- [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md)
|
||||
- [数据结构知识学习与面试](docs/dataStructures-algorithms/数据结构.md)
|
||||
1. [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md)
|
||||
2. [数据结构知识学习与面试](docs/dataStructures-algorithms/数据结构.md)
|
||||
|
||||
### 算法
|
||||
|
||||
- [硬核的算法学习书籍+资源推荐](docs/dataStructures-algorithms/算法学习资源推荐.md)
|
||||
- 常见算法问题总结:
|
||||
- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md)
|
||||
- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md)
|
||||
- [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md)
|
||||
- [公司真题](docs/dataStructures-algorithms/公司真题.md)
|
||||
- [回溯算法经典案例之 N 皇后问题](docs/dataStructures-algorithms/Backtracking-NQueens.md)
|
||||
算法这部分内容非常重要,如果你不知道如何学习算法的话,可以看下我写的:[《硬核的算法学习书籍+资源推荐》](docs/dataStructures-algorithms/算法学习资源推荐.md) 。
|
||||
|
||||
**常见算法问题总结:**
|
||||
|
||||
- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md)
|
||||
- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md)
|
||||
- [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md)
|
||||
|
||||
## 数据库
|
||||
|
||||
@ -228,88 +209,70 @@
|
||||
|
||||
### Redis
|
||||
|
||||
- [关于缓存的一些重要概念(Redis 前置菜)](docs/database/Redis/some-concepts-of-caching.md)
|
||||
- [Redis 常见问题总结](docs/database/Redis/redis-all.md)
|
||||
1. [关于缓存的一些重要概念(Redis 前置菜)](docs/database/Redis/some-concepts-of-caching.md)
|
||||
2. [Redis 常见问题总结](docs/database/Redis/redis-all.md)
|
||||
|
||||
## 系统设计
|
||||
|
||||
### 必知
|
||||
### 编码之道(必看 :+1:)
|
||||
|
||||
1. **[RestFul API 简明教程](docs/system-design/restful-api.md)**
|
||||
2. **[因为命名被 diss 无数次。Guide 简单聊聊编程最头疼的事情之一:命名](docs/system-design/naming.md)**
|
||||
1. [RestFul API 简明教程](docs/system-design/coding-way/RESTfulAPI简明教程.md)
|
||||
2. [Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md)
|
||||
3. [Java 命名之道](docs/system-design/naming.md)
|
||||
|
||||
### 常用框架
|
||||
|
||||
#### Spring/SpringBoot
|
||||
如果你没有接触过 Java Web 开发的话,可以先看一下我总结的 [《J2EE 基础知识》](docs/java/J2EE基础知识.md) 。虽然,这篇文章中的很多内容已经淘汰,但是可以让你对 Java 后台技术发展有更深的认识。
|
||||
|
||||
#### Spring/SpringBoot (必看 :+1:)
|
||||
|
||||
**知识点/面试题:**
|
||||
|
||||
1. **[Spring 常见问题总结](docs/system-design/framework/spring/SpringInterviewQuestions.md)**
|
||||
2. **[SpringBoot 指南/常见面试题总结](https://github.com/Snailclimb/springboot-guide)**
|
||||
3. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/spring-annotations.md)**
|
||||
4. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)**
|
||||
5. [Spring IoC 和 AOP 详解](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486938&idx=1&sn=c99ef0233f39a5ffc1b98c81e02dfcd4&chksm=cea24211f9d5cb07fa901183ba4d96187820713a72387788408040822ffb2ed575d28e953ce7&token=1666190828&lang=zh_CN#rd)
|
||||
6. [Spring 中 Bean 的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md)
|
||||
7. [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md)
|
||||
8. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md)
|
||||
|
||||
**重要知识点详解:**
|
||||
|
||||
1. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md)**
|
||||
2. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)**
|
||||
3. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md)
|
||||
|
||||
#### MyBatis
|
||||
|
||||
- [MyBatis 常见面试题总结](docs/system-design/framework/mybatis/mybatis-interview.md)
|
||||
|
||||
#### Netty
|
||||
#### Netty (必看 :+1:)
|
||||
|
||||
1. [剖析面试最常见问题之 Netty(上)](https://xiaozhuanlan.com/topic/4028536971)
|
||||
2. [剖析面试最常见问题之 Netty(下)](https://xiaozhuanlan.com/topic/3985146207)
|
||||
|
||||
### 认证授权
|
||||
|
||||
**[认证授权基础:搞清 Authentication,Authorization 以及 Cookie、Session、Token、OAuth 2、SSO](docs/system-design/authority-certification/basis-of-authority-certification.md)**
|
||||
**[《认证授权基础》](docs/system-design/authority-certification/basis-of-authority-certification.md)** 这篇文章中我会介绍认证授权常见概念: **Authentication**,**Authorization** 以及 **Cookie**、**Session**、Token、**OAuth 2**、**SSO** 。如果你不清楚这些概念的话,建议好好阅读一下这篇文章。
|
||||
|
||||
#### JWT
|
||||
|
||||
- **[JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT-advantages-and-disadvantages.md)**
|
||||
- **[适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)**
|
||||
1. [JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT优缺点分析以及常见问题解决方案.md)
|
||||
2. [适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)
|
||||
|
||||
#### SSO(单点登录)
|
||||
|
||||
SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。相关阅读:**[SSO 单点登录看这篇就够了!](docs/system-design/authority-certification/sso.md)**
|
||||
**SSO(Single Sign On)** 即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。相关阅读:**[SSO 单点登录看这篇就够了!](docs/system-design/authority-certification/SSO单点登录看这一篇就够了.md)**
|
||||
|
||||
### 分布式
|
||||
|
||||
[分布式相关概念入门](docs/system-design/website-architecture/分布式.md)
|
||||
[分布式相关概念入门](docs/system-design/distributed-system/分布式.md)
|
||||
|
||||
#### 分布式搜索引擎
|
||||
#### 搜索引擎
|
||||
|
||||
提高搜索效率。常见于电商购物网站的商品搜索于分类。
|
||||
|
||||
比较常用的是 Elasticsearch 和 Solr。
|
||||
|
||||
代办。
|
||||
用于提高搜索效率,功能和浏览器搜索引擎类似。比较常见的搜索引擎是 Elasticsearch(推荐) 和 Solr。
|
||||
|
||||
#### RPC
|
||||
|
||||
让调用远程服务调用像调用本地方法那样简单。
|
||||
RPC 让调用远程服务调用像调用本地方法那样简单。
|
||||
|
||||
- [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/data-communication/dubbo.md)
|
||||
- [服务之间的调用为啥不直接用 HTTP 而用 RPC?](docs/system-design/data-communication/why-use-rpc.md)
|
||||
|
||||
#### 消息队列
|
||||
|
||||
消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: **[消息队列总结](docs/system-design/data-communication/message-queue.md)** 。
|
||||
|
||||
**RabbitMQ:**
|
||||
|
||||
1. [RabbitMQ 入门](docs/system-design/data-communication/rabbitmq.md)
|
||||
|
||||
**RocketMQ:**
|
||||
|
||||
1. [RocketMQ 入门](docs/system-design/data-communication/RocketMQ.md)
|
||||
2. [RocketMQ 的几个简单问题与答案](docs/system-design/data-communication/RocketMQ-Questions.md)
|
||||
|
||||
**Kafka:**
|
||||
|
||||
1. **[Kafka 入门+SpringBoot 整合 Kafka 系列](https://github.com/Snailclimb/springboot-kafka)**
|
||||
2. **[Kafka 常见面试题总结](docs/system-design/data-communication/kafka-inverview.md)**
|
||||
3. [【加餐】Kafka 入门看这一篇就够了](docs/system-design/data-communication/Kafka入门看这一篇就够了.md)
|
||||
1. [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/distributed-system/rpc/关于Dubbo的重要知识点.md)
|
||||
2. [服务之间的调用为啥不直接用 HTTP 而用 RPC?](docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md)
|
||||
|
||||
#### API 网关
|
||||
|
||||
@ -320,69 +283,99 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中
|
||||
|
||||
#### 分布式 id
|
||||
|
||||
1. [为什么要分布式 id ?分布式 id 生成方案有哪些?](docs/system-design/micro-service/分布式id生成方案总结.md)
|
||||
|
||||
#### 分布式限流
|
||||
|
||||
1. [限流算法有哪些?](docs/system-design/micro-service/limit-request.md)
|
||||
|
||||
#### 分布式接口幂等性
|
||||
在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。比如数据量太大之后,往往需要对进行对数据进行分库分表,分库分表后需要有一个唯一 ID 来标识一条数据或消息,数据库的自增 ID 显然不能满足需求。相关阅读:[为什么要分布式 id ?分布式 id 生成方案有哪些?](docs/system-design/micro-service/分布式id生成方案总结.md)
|
||||
|
||||
#### ZooKeeper
|
||||
|
||||
> 前两篇文章可能有内容重合部分,推荐都看一遍。
|
||||
|
||||
1. [【入门】ZooKeeper 相关概念总结 01](docs/system-design/framework/zookeeper/zookeeper-intro.md)
|
||||
2. [【进阶】ZooKeeper 相关概念总结 02](docs/system-design/framework/zookeeper/zookeeper-plus.md)
|
||||
1. [【入门】ZooKeeper 相关概念总结](docs/system-design/framework/zookeeper/zookeeper-intro.md)
|
||||
2. [【进阶】ZooKeeper 相关概念总结](docs/system-design/framework/zookeeper/zookeeper-plus.md)
|
||||
3. [【实战】ZooKeeper 实战](docs/system-design/framework/zookeeper/zookeeper-in-action.md)
|
||||
|
||||
#### 其他
|
||||
### 微服务
|
||||
|
||||
- 接口幂等性(代办):分布式系统必须要考虑接口的幂等性。
|
||||
1. [ 大白话入门 Spring Cloud](docs/system-design/micro-service/spring-cloud.md)
|
||||
2. [微服务/分布式大厂真实面试问题解答](https://xiaozhuanlan.com/topic/2895047136)
|
||||
|
||||
#### 数据库扩展
|
||||
### 高并发
|
||||
|
||||
读写分离、分库分表。
|
||||
#### 消息队列
|
||||
|
||||
代办.....
|
||||
消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: **[消息队列总结](docs/system-design/data-communication/message-queue.md)** 。
|
||||
|
||||
1. **RabbitMQ** : [RabbitMQ 入门](docs/system-design/distributed-system/message-queue/RabbitMQ入门看这一篇就够了.md)
|
||||
2. **RocketMQ** : [RocketMQ 入门](docs/system-design/distributed-system/message-queue/RocketMQ.md)、[RocketMQ 的几个简单问题与答案](docs/system-design/distributed-system/message-queue/RocketMQ-Questions.md)
|
||||
3. **Kafka** :**[Kafka 常见面试题总结](docs/system-design/distributed-system/message-queue/Kafka常见面试题总结.md)**
|
||||
|
||||
#### 读写分离
|
||||
|
||||
读写分离主要是为了将数据库的读和写操作分不到不同的数据库节点上。主服务器负责写,从服务器负责读。另外,一主一从或者一主多从都可以。
|
||||
|
||||
**读写分离可以大幅提高读性能,小幅提高写的性能。因此,读写分离更适合单机并发读请求比较多的场景。**
|
||||
|
||||
#### 分库分表
|
||||
|
||||
**分库分表是为了解决由于库、表数据量过大,而导致数据库性能持续下降的问题。** 常见的分库分表工具有:`sharding-jdbc`(当当)、`TSharding`(蘑菇街)、`MyCAT`(基于 Cobar)、`Cobar`(阿里巴巴)...。
|
||||
|
||||
**推荐使用 `sharding-jdbc`** 。 因为,`sharding-jdbc` 是一款轻量级 `Java` 框架,以 `jar` 包形式提供服务,不要我们做额外的运维工作,并且兼容性也很好。
|
||||
|
||||
#### 负载均衡
|
||||
|
||||
负载均衡系统通常用于将任务比如用户请求处理分配到多个服务器处理以提高网站、应用或者数据库的性能和可靠性。
|
||||
|
||||
常见的负载均衡系统包括 3 种:
|
||||
|
||||
1. **DNS 负载均衡** :一般用来实现地理级别的均衡。
|
||||
2. **硬件负载均衡** : 通过单独的硬件设备比如 F5 来实现负载均衡功能(硬件的价格一般很贵)。
|
||||
3. **软件负载均衡** :通过负载均衡软件比如 Nginx 来实现负载均衡功能。
|
||||
|
||||
### 高可用
|
||||
|
||||
高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。
|
||||
|
||||
相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/system-design/high-availability/如何设计一个高可用系统?要考虑哪些地方?.md)》** 。
|
||||
|
||||
#### CAP 理论
|
||||
|
||||
CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。
|
||||
|
||||
关于 CAP 的详细解读请看:[《CAP理论解读》](docs/system-design/high-availability/CAP理论.md)。
|
||||
|
||||
#### BASE 理论
|
||||
|
||||
**BASE** 是 **Basically Available(基本可用)** 、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。
|
||||
|
||||
关于 CAP 的详细解读请看:[《BASE理论解读》](docs/system-design/high-availability/BASE理论.md)。
|
||||
|
||||
#### 限流
|
||||
|
||||
限流为了对服务端的接口接受请求的频率进行限制,防止服务挂掉。比如某一接口的请求限制为 100 个每秒, 对超过限制的请求放弃处理或者放到队列中等待处理。限流可以有效应对突发请求过多。相关阅读:[限流算法有哪些?](docs/system-design/high-availability/limit-request.md)
|
||||
|
||||
#### 降级
|
||||
|
||||
限流是从用户访问压力的角度来考虑如何应对故障,降级是从系统功能优先级的角度考虑如何应对故障
|
||||
|
||||
服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。降级往往会指定不同的级别,面临不同的异常等级执行不同的处理。根据服务方式:可以拒接服务,可以延迟服务,也有时候可以随机服务。根据服务范围:可以砍掉某个功能,也可以砍掉某些模块。总之服务降级需要根据不同的业务需求采用不同的降级策略。主要的目的就是服务虽然有损但是总比没有好。
|
||||
|
||||
#### 熔断
|
||||
|
||||
熔断和降级是两个比较容易混淆的概念,因为单纯从名字上看好像都有禁止某个功能的意思,但其实内在含义是不同的,原因在于降级的目的是应对系统自身的故障,而熔断的目的是应对依赖的外部系统故障的情况。
|
||||
|
||||
#### 排队
|
||||
|
||||
另类的一种限流,类比于现实世界的排队。玩过英雄联盟的小伙伴应该有体会,每次一有活动,就要经历一波排队才能进入游戏。
|
||||
|
||||
### 大型网站架构
|
||||
|
||||
- [8 张图读懂大型网站技术架构](docs/system-design/website-architecture/8%20张图读懂大型网站技术架构.md)
|
||||
- [关于大型网站系统架构你不得不懂的 10 个问题](docs/system-design/website-architecture/关于大型网站系统架构你不得不懂的10个问题.md)
|
||||
|
||||
#### 性能测试
|
||||
## 工具
|
||||
|
||||
- [后端程序员也要懂的性能测试知识](https://articles.zsxq.com/id_lwl39teglv3d.html) (知识星球)
|
||||
|
||||
#### 高并发
|
||||
|
||||
待办......
|
||||
|
||||
#### 高可用
|
||||
|
||||
高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/system-design/website-architecture/如何设计一个高可用系统?要考虑哪些地方?.md)》** 。
|
||||
|
||||
### 微服务
|
||||
|
||||
#### Spring Cloud
|
||||
|
||||
- [ 大白话入门 Spring Cloud](docs/system-design/micro-service/spring-cloud.md)
|
||||
|
||||
## 必会工具
|
||||
|
||||
### Git
|
||||
|
||||
- [Git 入门](docs/tools/Git.md)
|
||||
|
||||
### Docker
|
||||
|
||||
1. [Docker 基本概念解读](docs/tools/Docker.md)
|
||||
2. [一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md)
|
||||
|
||||
### 其他
|
||||
|
||||
- [【原创】如何使用云服务器?希望这篇文章能够对你有帮助!](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485738&idx=1&sn=f97e91a50e444944076c30b0717b303a&chksm=cea246e1f9d5cff73faf6a778b147ea85162d1f3ed55ca90473c6ebae1e2c4d13e89282aeb24&token=406194678&lang=zh_CN#rd)
|
||||
1. **Java** :[JAD 反编译](docs/java/JAD反编译tricks.md)、[手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md)
|
||||
2. **Git** :[Git 入门](docs/tools/Git.md)
|
||||
3. **Docker** : [Docker 基本概念解读](docs/tools/Docker.md) 、[一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md)
|
||||
|
||||
## 面试指南
|
||||
|
||||
@ -403,51 +396,31 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中
|
||||
3. [新手学习 Java,有哪些 Java 相关的博客,专栏,和技术学习网站推荐?](docs/questions/java-learning-website-blog.md)
|
||||
4. [Java 还是大数据,你需要了解这些东西!](docs/questions/java-big-data.md)
|
||||
|
||||
## 资源
|
||||
## 书单
|
||||
|
||||
### Java 程序员必备书单
|
||||
|
||||
1. [「基础篇」Guide 的 Java 后端书架来啦!都是 Java 程序员必看的书籍?](./docs/books/java基础篇.md)
|
||||
|
||||
### 实战项目推荐
|
||||
|
||||
- **[Java、SpringBoot 实战项目推荐](https://github.com/Snailclimb/awesome-java#实战项目)**
|
||||
|
||||
### Github
|
||||
|
||||
- [Github 上非常棒的 Java 开源项目集合](https://github.com/Snailclimb/awesome-java)
|
||||
- [Github 上 Star 数最多的 10 个项目,看完之后很意外!](docs/tools/github/github-star-ranking.md)
|
||||
- [年末将至,值得你关注的 16 个 Java 开源项目!](docs/github-trending/2019-12.md)
|
||||
- [Java 项目历史月榜单](docs/github-trending/JavaGithubTrending.md)
|
||||
1. [「基础篇」Java 书单](./docs/books/java基础篇.md)
|
||||
|
||||
---
|
||||
|
||||
## 待办
|
||||
## 其他
|
||||
|
||||
### 待办
|
||||
|
||||
- [x] Netty 总结
|
||||
- [ ] 数据结构总结重构(---正在进行中---)
|
||||
- [ ] 数据结构总结重构
|
||||
- [ ] 将 JavaGuide 的基础知识部分抽出来单独弄一个 CS-Guide
|
||||
|
||||
## 说明
|
||||
|
||||
开源项目在于大家的参与,这才使得它的价值得到提升。感谢 🙏 有你!
|
||||
|
||||
项目的 Markdown 格式参考:[Github Markdown 格式](https://guides.github.com/features/mastering-markdown/),表情素材来自:[EMOJI CHEAT SHEET](https://www.webpagefx.com/tools/emoji-cheat-sheet/)。
|
||||
|
||||
利用 docsify 生成文档部署在 Github pages: [docsify 官网介绍](https://docsify.js.org/#/) ,另见[《Guide 哥手把手教你搭建一个文档类型的网站!免费且高速!》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486555&idx=2&sn=8486026ee9f9ba645ff0363df6036184&chksm=cea24390f9d5ca86ff4177c0aca5e719de17dc89e918212513ee661dd56f17ca8269f4a6e303&token=298703358&lang=zh_CN#rd) 。
|
||||
|
||||
Logo 下的小图标是使用[Shields.IO](https://shields.io/) 生成的。
|
||||
|
||||
## 联系我
|
||||
### 联系我
|
||||
|
||||

|
||||
|
||||
## 捐赠支持
|
||||
### 捐赠支持
|
||||
|
||||
项目的发展离不开你的支持,如果 JavaGuide 帮助到了你找到自己满意的 offer,请作者喝杯咖啡吧 ☕ 后续会继续完善更新!加油!
|
||||
|
||||
[点击捐赠支持作者](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok)
|
||||
|
||||
## Contributor
|
||||
### Contributor
|
||||
|
||||
下面是笔主收集的一些对本仓库提过有价值的 pr 或者 issue 的朋友,人数较多,如果你也对本仓库提过不错的 pr 或者 issue 的话,你可以加我的微信与我联系。下面的排名不分先后!
|
||||
|
||||
@ -502,7 +475,7 @@ Logo 下的小图标是使用[Shields.IO](https://shields.io/) 生成的。
|
||||
<img src="https://avatars0.githubusercontent.com/u/20358122?s=460&v=4" width="45px">
|
||||
</a>
|
||||
|
||||
## 公众号
|
||||
### 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
|
@ -1,145 +0,0 @@
|
||||
# N皇后
|
||||
[51. N皇后](https://leetcode-cn.com/problems/n-queens/)
|
||||
### 题目描述
|
||||
> n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
|
||||
>
|
||||

|
||||
>
|
||||
上图为 8 皇后问题的一种解法。
|
||||
>
|
||||
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
|
||||
>
|
||||
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
|
||||
|
||||
示例:
|
||||
|
||||
```
|
||||
输入: 4
|
||||
输出: [
|
||||
[".Q..", // 解法 1
|
||||
"...Q",
|
||||
"Q...",
|
||||
"..Q."],
|
||||
|
||||
["..Q.", // 解法 2
|
||||
"Q...",
|
||||
"...Q",
|
||||
".Q.."]
|
||||
]
|
||||
解释: 4 皇后问题存在两个不同的解法。
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
约束条件为每个棋子所在的行、列、对角线都不能有另一个棋子。
|
||||
|
||||
使用一维数组表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
|
||||
每行只存储一个元素,然后递归到下一行,这样就不用判断行了,只需要判断列和对角线。
|
||||
### Solution1
|
||||
当result[row] = column时,即row行的棋子在column列。
|
||||
|
||||
对于[0, row-1]的任意一行(i 行),若 row 行的棋子和 i 行的棋子在同一列,则有result[i] == column;
|
||||
若 row 行的棋子和 i 行的棋子在同一对角线,等腰直角三角形两直角边相等,即 row - i == Math.abs(result[i] - column)
|
||||
|
||||
布尔类型变量 isValid 的作用是剪枝,减少不必要的递归。
|
||||
```java
|
||||
public List<List<String>> solveNQueens(int n) {
|
||||
// 下标代表行,值代表列。如result[0] = 3 表示第1行的Q在第3列
|
||||
int[] result = new int[n];
|
||||
List<List<String>> resultList = new LinkedList<>();
|
||||
dfs(resultList, result, 0, n);
|
||||
return resultList;
|
||||
}
|
||||
|
||||
void dfs(List<List<String>> resultList, int[] result, int row, int n) {
|
||||
// 递归终止条件
|
||||
if (row == n) {
|
||||
List<String> list = new LinkedList<>();
|
||||
for (int x = 0; x < n; ++x) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int y = 0; y < n; ++y)
|
||||
sb.append(result[x] == y ? "Q" : ".");
|
||||
list.add(sb.toString());
|
||||
}
|
||||
resultList.add(list);
|
||||
return;
|
||||
}
|
||||
for (int column = 0; column < n; ++column) {
|
||||
boolean isValid = true;
|
||||
result[row] = column;
|
||||
/*
|
||||
* 逐行往下考察每一行。同列,result[i] == column
|
||||
* 同对角线,row - i == Math.abs(result[i] - column)
|
||||
*/
|
||||
for (int i = row - 1; i >= 0; --i) {
|
||||
if (result[i] == column || row - i == Math.abs(result[i] - column)) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid) dfs(resultList, result, row + 1, n);
|
||||
}
|
||||
}
|
||||
```
|
||||
### Solution2
|
||||
使用LinkedList表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
|
||||
|
||||
解法二和解法一的不同在于,相同列以及相同对角线的校验。
|
||||
将对角线抽象成【一次函数】这个简单的数学模型,根据一次函数的截距是常量这一特性进行校验。
|
||||
|
||||
这里,我将右上-左下对角线,简称为“\”对角线;左上-右下对角线简称为“/”对角线。
|
||||
|
||||
“/”对角线斜率为1,对应方程为y = x + b,其中b为截距。
|
||||
对于线上任意一点,均有y - x = b,即row - i = b;
|
||||
定义一个布尔类型数组anti_diag,将b作为下标,当anti_diag[b] = true时,表示相应对角线上已经放置棋子。
|
||||
但row - i有可能为负数,负数不能作为数组下标,row - i 的最小值为-n(当row = 0,i = n时),可以加上n作为数组下标,即将row -i + n 作为数组下标。
|
||||
row - i + n 的最大值为 2n(当row = n,i = 0时),故anti_diag的容量设置为 2n 即可。
|
||||
|
||||

|
||||
|
||||
“\”对角线斜率为-1,对应方程为y = -x + b,其中b为截距。
|
||||
对于线上任意一点,均有y + x = b,即row + i = b;
|
||||
同理,定义数组main_diag,将b作为下标,当main_diag[row + i] = true时,表示相应对角线上已经放置棋子。
|
||||
|
||||
有了两个校验对角线的数组,再来定义一个用于校验列的数组cols,这个太简单啦,不解释。
|
||||
|
||||
**解法二时间复杂度为O(n!),在校验相同列和相同对角线时,引入三个布尔类型数组进行判断。相比解法一,少了一层循环,用空间换时间。**
|
||||
|
||||
```java
|
||||
List<List<String>> resultList = new LinkedList<>();
|
||||
|
||||
public List<List<String>> solveNQueens(int n) {
|
||||
boolean[] cols = new boolean[n];
|
||||
boolean[] main_diag = new boolean[2 * n];
|
||||
boolean[] anti_diag = new boolean[2 * n];
|
||||
LinkedList<Integer> result = new LinkedList<>();
|
||||
dfs(result, 0, cols, main_diag, anti_diag, n);
|
||||
return resultList;
|
||||
}
|
||||
|
||||
void dfs(LinkedList<Integer> result, int row, boolean[] cols, boolean[] main_diag, boolean[] anti_diag, int n) {
|
||||
if (row == n) {
|
||||
List<String> list = new LinkedList<>();
|
||||
for (int x = 0; x < n; ++x) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int y = 0; y < n; ++y)
|
||||
sb.append(result.get(x) == y ? "Q" : ".");
|
||||
list.add(sb.toString());
|
||||
}
|
||||
resultList.add(list);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (cols[i] || main_diag[row + i] || anti_diag[row - i + n])
|
||||
continue;
|
||||
result.add(i);
|
||||
cols[i] = true;
|
||||
main_diag[row + i] = true;
|
||||
anti_diag[row - i + n] = true;
|
||||
dfs(result, row + 1, cols, main_diag, anti_diag, n);
|
||||
result.removeLast();
|
||||
cols[i] = false;
|
||||
main_diag[row + i] = false;
|
||||
anti_diag[row - i + n] = false;
|
||||
}
|
||||
}
|
||||
```
|
BIN
docs/dataStructures-algorithms/images/我的第一本算法书.jpeg
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 128 KiB |
BIN
docs/dataStructures-algorithms/images/程序员代码面试指南.jpeg
Normal file
After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 119 KiB |
BIN
docs/dataStructures-algorithms/images/算法-4.jpeg
Normal file
After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 59 KiB |
BIN
docs/dataStructures-algorithms/images/算法图解.jpeg
Normal file
After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 72 KiB |
BIN
docs/dataStructures-algorithms/images/算法导论.jpeg
Normal file
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 413 KiB After Width: | Height: | Size: 208 KiB |
BIN
docs/dataStructures-algorithms/images/编程之美.jpeg
Normal file
After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 150 KiB |
BIN
docs/dataStructures-algorithms/images/编程珠玑.jpeg
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 71 KiB |
@ -1,254 +0,0 @@
|
||||
# 网易 2018
|
||||
|
||||
下面三道编程题来自网易2018校招编程题,这三道应该来说是非常简单的编程题了,这些题目大家稍微有点编程和数学基础的话应该没什么问题。看答案之前一定要自己先想一下如果是自己做的话会怎么去做,然后再对照这我的答案看看,和你自己想的有什么区别?那一种方法更好?
|
||||
|
||||
## 问题
|
||||
|
||||
### 一 获得特定数量硬币问题
|
||||
|
||||
小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币,但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。
|
||||
|
||||
魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币
|
||||
|
||||
魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币
|
||||
|
||||
小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币,小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。
|
||||
|
||||
**输入描述:** 输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。
|
||||
|
||||
**输出描述:** 输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。
|
||||
|
||||
**输入例子1:** 10
|
||||
|
||||
**输出例子1:** 122
|
||||
|
||||
### 二 求“相反数”问题
|
||||
|
||||
为了得到一个数的"相反数",我们将这个数的数字顺序颠倒,然后再加上原先的数得到"相反数"。例如,为了得到1325的"相反数",首先我们将该数的数字顺序颠倒,我们得到5231,之后再加上原先的数,我们得到5231+1325=6556.如果颠倒之后的数字有前缀零,前缀零将会被忽略。例如n = 100, 颠倒之后是1.
|
||||
|
||||
**输入描述:** 输入包括一个整数n,(1 ≤ n ≤ 10^5)
|
||||
|
||||
**输出描述:** 输出一个整数,表示n的相反数
|
||||
|
||||
**输入例子1:** 1325
|
||||
|
||||
**输出例子1:** 6556
|
||||
|
||||
### 三 字符串碎片的平均长度
|
||||
|
||||
一个由小写字母组成的字符串可以看成一些同一字母的最大碎片组成的。例如,"aaabbaaac"是由下面碎片组成的:'aaa','bb','c'。牛牛现在给定一个字符串,请你帮助计算这个字符串的所有碎片的平均长度是多少。
|
||||
|
||||
**输入描述:** 输入包括一个字符串s,字符串s的长度length(1 ≤ length ≤ 50),s只含小写字母('a'-'z')
|
||||
|
||||
**输出描述:** 输出一个整数,表示所有碎片的平均长度,四舍五入保留两位小数。
|
||||
|
||||
**如样例所示:** s = "aaabbaaac"
|
||||
所有碎片的平均长度 = (3 + 2 + 3 + 1) / 4 = 2.25
|
||||
|
||||
**输入例子1:** aaabbaaac
|
||||
|
||||
**输出例子1:** 2.25
|
||||
|
||||
## 答案
|
||||
|
||||
### 一 获得特定数量硬币问题
|
||||
|
||||
#### 分析:
|
||||
|
||||
作为该试卷的第一题,这道题应该只要思路正确就很简单了。
|
||||
|
||||
解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
#### 示例代码
|
||||
|
||||
注意:由于用户的输入不确定性,一般是为了程序高可用性使需要将捕获用户输入异常然后友好提示用户输入类型错误并重新输入的。所以下面我给了两个版本,这两个版本都是正确的。这里只是给大家演示如何捕获输入类型异常,后面的题目中我给的代码没有异常处理的部分,参照下面两个示例代码,应该很容易添加。(PS:企业面试中没有明确就不用添加异常处理,当然你有的话也更好)
|
||||
|
||||
**不带输入异常处理判断的版本:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main2 {
|
||||
// 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("请输入要获得的硬币数量:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int coincount = scanner.nextInt();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (coincount >= 1) {
|
||||
// 偶数的情况
|
||||
if (coincount % 2 == 0) {
|
||||
coincount = (coincount - 2) / 2;
|
||||
sb.append("2");
|
||||
// 奇数的情况
|
||||
} else {
|
||||
coincount = (coincount - 1) / 2;
|
||||
sb.append("1");
|
||||
}
|
||||
}
|
||||
// 输出反转后的字符串
|
||||
System.out.println(sb.reverse());
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**带输入异常处理判断的版本(当输入的不是整数的时候会提示重新输入):**
|
||||
|
||||
```java
|
||||
import java.util.InputMismatchException;
|
||||
import java.util.Scanner;
|
||||
|
||||
|
||||
public class Main {
|
||||
// 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("请输入要获得的硬币数量:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
boolean flag = true;
|
||||
while (flag) {
|
||||
try {
|
||||
int coincount = scanner.nextInt();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (coincount >= 1) {
|
||||
// 偶数的情况
|
||||
if (coincount % 2 == 0) {
|
||||
coincount = (coincount - 2) / 2;
|
||||
sb.append("2");
|
||||
// 奇数的情况
|
||||
} else {
|
||||
coincount = (coincount - 1) / 2;
|
||||
sb.append("1");
|
||||
}
|
||||
}
|
||||
// 输出反转后的字符串
|
||||
System.out.println(sb.reverse());
|
||||
flag=false;//程序结束
|
||||
} catch (InputMismatchException e) {
|
||||
System.out.println("输入数据类型不匹配,请您重新输入:");
|
||||
scanner.nextLine();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 二 求“相反数”问题
|
||||
|
||||
#### 分析:
|
||||
|
||||
解决本道题有几种不同的方法,但是最快速的方法就是利用reverse()方法反转字符串然后再将字符串转换成int类型的整数,这个方法是快速解决本题关键。我们先来回顾一下下面两个知识点:
|
||||
|
||||
**1)String转int;**
|
||||
|
||||
在 Java 中要将 String 类型转化为 int 类型时,需要使用 Integer 类中的 parseInt() 方法或者 valueOf() 方法进行转换.
|
||||
|
||||
```java
|
||||
String str = "123";
|
||||
int a = Integer.parseInt(str);
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```java
|
||||
String str = "123";
|
||||
int a = Integer.valueOf(str).intValue();
|
||||
```
|
||||
|
||||
**2)next()和nextLine()的区别**
|
||||
|
||||
在Java中输入字符串有两种方法,就是next()和nextLine().两者的区别就是:nextLine()的输入是碰到回车就终止输入,而next()方法是碰到空格,回车,Tab键都会被视为终止符。所以next()不会得到带空格的字符串,而nextLine()可以得到带空格的字符串。
|
||||
|
||||
#### 示例代码:
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* 本题关键:①String转int;②next()和nextLine()的区别
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("请输入一个整数:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String s=scanner.next();
|
||||
//将字符串转换成数字
|
||||
int number1=Integer.parseInt(s);
|
||||
//将字符串倒序后转换成数字
|
||||
//因为Integer.parseInt()的参数类型必须是字符串所以必须加上toString()
|
||||
int number2=Integer.parseInt(new StringBuilder(s).reverse().toString());
|
||||
System.out.println(number1+number2);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 三 字符串碎片的平均长度
|
||||
|
||||
#### 分析:
|
||||
|
||||
这道题的意思也就是要求:(字符串的总长度)/(相同字母团构成的字符串的个数)。
|
||||
|
||||
这样就很简单了,就变成了字符串的字符之间的比较。如果需要比较字符串的字符的话,我们可以利用charAt(i)方法:取出特定位置的字符与后一个字符比较,或者利用toCharArray()方法将字符串转换成字符数组采用同样的方法做比较。
|
||||
|
||||
#### 示例代码
|
||||
|
||||
**利用charAt(i)方法:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner sc = new Scanner(System.in);
|
||||
while (sc.hasNext()) {
|
||||
String s = sc.next();
|
||||
//个数至少为一个
|
||||
float count = 1;
|
||||
for (int i = 0; i < s.length() - 1; i++) {
|
||||
if (s.charAt(i) != s.charAt(i + 1)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
System.out.println(s.length() / count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**利用toCharArray()方法:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner sc = new Scanner(System.in);
|
||||
while (sc.hasNext()) {
|
||||
String s = sc.next();
|
||||
//个数至少为一个
|
||||
float count = 1;
|
||||
char [] stringArr = s.toCharArray();
|
||||
for (int i = 0; i < stringArr.length - 1; i++) {
|
||||
if (stringArr[i] != stringArr[i + 1]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
System.out.println(s.length() / count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
@ -129,7 +129,7 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且
|
||||
|
||||
### 4 二叉查找树(BST)
|
||||
|
||||
[浅谈算法和数据结构: 七 二叉查找树](http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html)
|
||||
[浅谈算法和数据结构: 七 二叉查找树](https://www.yycoding.xyz/post/2014/3/24/introduce-binary-search-tree)
|
||||
|
||||
二叉查找树的特点:
|
||||
|
||||
|
@ -12,13 +12,13 @@
|
||||
|
||||
### 入门
|
||||
|
||||
<img src="images/我的第一本算法书.png" style="zoom:33%;" />
|
||||

|
||||
|
||||
**[我的第一本算法书](https://book.douban.com/subject/30357170/) (豆瓣评分 7.1,0.2K+人评价)**
|
||||
|
||||
一本不那么“专业”的算法书籍。和下面两本推荐的算法书籍都是比较通俗易懂,“不那么深入”的算法书籍。我个人非常推荐,配图和讲解都非常不错!
|
||||
|
||||
<img src="images/算法图解.png" alt="img" style="zoom:50%;" />
|
||||

|
||||
|
||||
**[《算法图解》](https://book.douban.com/subject/26979890/)(豆瓣评分 8.4,1.5K+人评价)**
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
### 经典
|
||||
|
||||
<img src="images/算法-4.png" style="zoom:50%;" />
|
||||

|
||||
|
||||
**[《算法 第四版》](https://book.douban.com/subject/10432347/)(豆瓣评分 9.3,0.4K+人评价)**
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
|
||||
> **下面这些书籍都是经典中的经典,但是阅读起来难度也比较大,不做太多阐述,神书就完事了!推荐先看 《算法》,然后再选下面的书籍进行进一步阅读。不需要都看,找一本好好看或者找某本书的某一个章节知识点好好看。**
|
||||
|
||||
<img src="images/编程珠玑.png" style="zoom:67%;" />
|
||||

|
||||
|
||||
**[编程珠玑](https://book.douban.com/subject/3227098/)(豆瓣评分 9.1,2K+人评价)**
|
||||
|
||||
@ -50,15 +50,13 @@
|
||||
|
||||
很多人都说这本书不是教你具体的算法,而是教你一种编程的思考方式。这种思考方式不仅仅在编程领域适用,在其他同样适用。
|
||||
|
||||
|
||||
|
||||
<img src="images/算法设计手册.png" style="zoom:50%;" />
|
||||

|
||||
|
||||
**[《算法设计手册》](https://book.douban.com/subject/4048566/)(豆瓣评分9.1 , 45人评价)**
|
||||
|
||||
被 [Teach Yourself Computer Science](https://teachyourselfcs.com/) 强烈推荐的一本算法书籍。
|
||||
|
||||
<img src="images/算法导论.png" style="zoom:48%;" />
|
||||

|
||||
|
||||
**[《算法导论》](https://book.douban.com/subject/20432061/) (豆瓣评分 9.2,0.4K+人评价)**
|
||||
|
||||
@ -80,15 +78,13 @@
|
||||
|
||||
|
||||
|
||||
<img src="images/程序员代码面试指南.png" style="zoom:50%;" />
|
||||

|
||||
|
||||
**[程序员代码面试指南:IT名企算法与数据结构题目最优解(第2版)](https://book.douban.com/subject/30422021/) (豆瓣评分 8.7,0.2K+人评价)**
|
||||
|
||||
题目相比于《剑指 offer》 来说要难很多,题目涵盖面相比于《剑指 offer》也更加全面。全书一共有将近300道真实出现过的经典代码面试题。
|
||||
|
||||
|
||||
|
||||
<img src="images/编程之美.png" style="zoom:55%;" />
|
||||

|
||||
|
||||
|
||||
|
||||
|
@ -83,14 +83,14 @@ JDK 1.5 以后的 `AtomicStampedReference 类`就提供了此种能力,其中
|
||||
|
||||
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了`AtomicReference类`来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作。
|
||||
|
||||
### CAS与synchronized的使用情景
|
||||
### CAS与`synchronized`的使用情景
|
||||
|
||||
> **简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)**
|
||||
|
||||
1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
|
||||
1. 对于资源竞争较少(线程冲突较轻)的情况,使用`synchronized`同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
|
||||
2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。
|
||||
|
||||
补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。
|
||||
补充: Java并发编程这个领域中`synchronized`关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。`synchronized`的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。
|
||||
|
||||
## 公众号
|
||||
|
||||
|
@ -1,78 +0,0 @@
|
||||
本文数据统计于 1.1 号凌晨,由 SnailClimb 整理。
|
||||
|
||||
### 1. JavaGuide
|
||||
|
||||
- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
|
||||
- **star**: 18.2k
|
||||
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
|
||||
|
||||
### 2. mall
|
||||
|
||||
- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 3.3k
|
||||
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 3. advanced-java
|
||||
|
||||
- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **star**: 3.3k
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲
|
||||
|
||||
### 4. matrix
|
||||
|
||||
- **Github地址**:[https://github.com/Tencent/matrix](https://github.com/Tencent/matrix)
|
||||
- **star**: 2.5k
|
||||
- **介绍**: Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上。 Matrix 的目标是建立统一的应用性能接入框架,通过各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
|
||||
|
||||
### 5. miaosha
|
||||
|
||||
- **Github地址**:[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha)
|
||||
- **star**: 2.4k
|
||||
- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。
|
||||
|
||||
### 6. arthas
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 8.2k
|
||||
- **介绍**: Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。
|
||||
|
||||
### 7 spring-boot
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 32.6k
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
**关于Spring Boot官方的介绍:**
|
||||
|
||||
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
|
||||
|
||||
### 8. tutorials
|
||||
|
||||
- **Github地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
|
||||
- **star**: 10k
|
||||
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。
|
||||
|
||||
### 9. qmq
|
||||
|
||||
- **Github地址**:[https://github.com/qunarcorp/qmq](https://github.com/qunarcorp/qmq)
|
||||
- **star**: 1.1k
|
||||
- **介绍**: QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。
|
||||
|
||||
|
||||
### 10. symphony
|
||||
|
||||
- **Github地址**:[https://github.com/b3log/symphony](https://github.com/b3log/symphony)
|
||||
- **star**: 9k
|
||||
- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。
|
||||
|
||||
### 11. incubator-dubbo
|
||||
|
||||
- **Github地址**:[https://github.com/apache/incubator-dubbo](https://github.com/apache/incubator-dubbo)
|
||||
- **star**: 23.6k
|
||||
- **介绍**: 阿里开源的一个基于Java的高性能开源RPC框架。
|
||||
|
||||
### 12. apollo
|
||||
|
||||
- **Github地址**:[https://github.com/ctripcorp/apollo](https://github.com/ctripcorp/apollo)
|
||||
- **star**: 10k
|
||||
- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
|
@ -1,76 +0,0 @@
|
||||
### 1. JavaGuide
|
||||
|
||||
- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
|
||||
- **star**: 22.8k
|
||||
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
|
||||
|
||||
### 2. advanced-java
|
||||
|
||||
- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **star**: 7.9k
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲
|
||||
|
||||
### 3. fescar
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar)
|
||||
- **star**: 4.6k
|
||||
- **介绍**: 具有 **高性能** 和 **易用性** 的 **微服务架构** 的 **分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。
|
||||
|
||||
### 4. mall
|
||||
|
||||
- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 5.6 k
|
||||
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 5. miaosha
|
||||
|
||||
- **Github地址**:[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha)
|
||||
- **star**: 4.4k
|
||||
- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。
|
||||
|
||||
### 6. flink
|
||||
|
||||
- **Github地址**:[https://github.com/apache/flink](https://github.com/apache/flink)
|
||||
- **star**: 7.1 k
|
||||
- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。
|
||||
|
||||
### 7. cim
|
||||
|
||||
- **Github地址**:[https://github.com/crossoverJie/cim](https://github.com/crossoverJie/cim)
|
||||
- **star**: 1.8 k
|
||||
- **介绍**: cim(cross IM) 适用于开发者的即时通讯系统。
|
||||
|
||||
### 8. symphony
|
||||
|
||||
- **Github地址**:[https://github.com/b3log/symphony](https://github.com/b3log/symphony)
|
||||
- **star**: 10k
|
||||
- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。
|
||||
|
||||
### 9. spring-boot
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 32.6k
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
**关于Spring Boot官方的介绍:**
|
||||
|
||||
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
|
||||
|
||||
### 10. arthas
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 9.5k
|
||||
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
|
||||
|
||||
**概览:**
|
||||
|
||||
当你遇到以下类似问题而束手无策时,`Arthas`可以帮助你解决:
|
||||
|
||||
0. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
|
||||
1. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
|
||||
2. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
|
||||
3. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
|
||||
4. 是否有一个全局视角来查看系统的运行状况?
|
||||
5. 有什么办法可以监控到JVM的实时运行状态?
|
||||
|
||||
`Arthas`支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。
|
@ -1,144 +0,0 @@
|
||||
# 年末将至,值得你关注的16个Java 开源项目!
|
||||
|
||||
Star 的数量统计于 2019-12-29。
|
||||
|
||||
### 1.JavaGuide
|
||||
|
||||
Guide 哥大三开始维护的,目前算是纯 Java 类型项目中 Star 数量最多的项目了。但是,本仓库的价值远远(+N次 )比不上像 Spring Boot、Elasticsearch 等等这样非常非常非常优秀的项目。希望以后我也有能力为这些项目贡献一些有价值的代码。
|
||||
|
||||
- **Github 地址**:<https://github.com/Snailclimb/JavaGuide>
|
||||
- **Star**: 66.3k
|
||||
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
|
||||
|
||||
### 2.java-design-patterns
|
||||
|
||||
感觉还不错。根据官网介绍:
|
||||
|
||||
> 设计模式是程序员在设计应用程序或系统时可以用来解决常见问题的最佳形式化实践。 设计模式可以通过提供经过测试的,经过验证的开发范例来加快开发过程。 重用设计模式有助于防止引起重大问题的细微问题,并且还可以提高熟悉模式的编码人员和架构师的代码可读性。
|
||||
|
||||

|
||||
|
||||
- **Github 地址** : [https://github.com/iluwatar/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
|
||||
- **Star**: 53.8k
|
||||
- **介绍**: 用 Java 实现的设计模式。[https://java-design-patterns.com](https://java-design-patterns.com/)。
|
||||
|
||||
### 3.elasticsearch
|
||||
|
||||
搜索引擎界的扛把子,但不仅仅是搜素引擎那么简单。
|
||||
|
||||
- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
|
||||
- **Star**: 46.2k
|
||||
- **介绍**: 开源,分布式,RESTful 搜索引擎。
|
||||
|
||||
### 4.spring-boot
|
||||
|
||||
必须好好学啊,一定要好好学!现在 Java 后端新项目有不用 Spring Boot 开发的有吗?如果有的话,请把这个人的联系方式告诉我,我有很多话想给他交流交流!
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 34.8k (1,073 stars this month)
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
### 5.RxJava
|
||||
|
||||
这个没怎么用过,不做太多评价。
|
||||
|
||||
- **Github 地址** : [https://github.com/ReactiveX/RxJava](https://github.com/ReactiveX/RxJava)
|
||||
- **Star**: 41.5k
|
||||
- **介绍**: `RxJava` 是一个 基于事件流、实现异步操作的库。
|
||||
|
||||
### 6.advanced-java
|
||||
|
||||
本项目大部分内容来自中华石杉的一个课程,内容涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识,非常不错了!
|
||||
|
||||
- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**: 36.7k
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。
|
||||
|
||||
### 7.mall
|
||||
|
||||
很牛逼的实战项目,还附有详细的文档,作为毕设或者练手项目都再好不过了。
|
||||
|
||||
- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 27.6k
|
||||
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 8.okhttp
|
||||
|
||||
给我感觉是安卓项目中用的居多。当然,Java 后端项目也会经常用,但是一般使用 Spring Boot 进行开发的时候,如果需要远程调用的话建议使用 Spring 封装的 `RestTemplate `。
|
||||
|
||||
- **Github地址**:[https://github.com/square/okhttp](https://github.com/square/okhttp)
|
||||
- **star**: 35.4k
|
||||
- **介绍**: 适用于Android,Kotlin和Java的HTTP客户端。https://square.github.io/okhttp/。
|
||||
|
||||
### 9.guava
|
||||
|
||||
很厉害很厉害!提供了很多非常实用的工具类、更加实用的集合类、一些常用的数据结构比如布隆过滤器、缓存等等。
|
||||
|
||||
- **Github地址**:[https://github.com/google/guava](https://github.com/google/guava)
|
||||
- **star**: 35.3k
|
||||
- **介绍**: Guava是一组核心库,其中包括新的集合类型(例如 multimap 和 multiset),不可变集合,图形库以及用于并发,I / O,哈希,基元,字符串等的实用程序!
|
||||
|
||||
### 10.Spark
|
||||
|
||||
我木有用过,留下了没有技术的眼泪。
|
||||
|
||||
- **Github地址**:[https://github.com/apache/spark](https://github.com/apache/spark)
|
||||
- **star**: 24.7k
|
||||
- **介绍**: Spark 是一个快速、通用的大规模数据处理引擎,和Hadoop的MapReduce计算框架类似,但是相对于MapReduce,Spark凭借其可伸缩、基于内存计算等特点,以及可以直接读写Hadoop上任何格式数据的优势,进行批处理时更加高效,并有更低的延迟。
|
||||
|
||||
### 11.arthas
|
||||
|
||||
虽然我自己没有亲身用过,但是身边用过的朋友评价都还挺好的。根据官网介绍,这家伙可以解决下面这些让人脑壳疼的问题:
|
||||
|
||||
1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
|
||||
2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
|
||||
3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
|
||||
4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
|
||||
5. 是否有一个全局视角来查看系统的运行状况?
|
||||
6. 有什么办法可以监控到JVM的实时运行状态?
|
||||
7. 怎么快速定位应用的热点,生成火焰图?
|
||||
|
||||
- **Github 地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 18.8 k
|
||||
- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。
|
||||
|
||||
### 12.spring-boot-examples
|
||||
|
||||
学习 Spring Boot 必备!配合上我的 **springboot-guide** :[https://github.com/Snailclimb/springboot-guide](https://github.com/Snailclimb/springboot-guide),效果杠杠滴!
|
||||
|
||||
- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
|
||||
- **star**: 20.2 k
|
||||
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
|
||||
|
||||
### 13.lombok
|
||||
|
||||
使用 Lombok 我们可以简化我们的 Java 代码,比如使用它之后我们通过注释就可以实现 getter/setter、equals等方法。
|
||||
|
||||
- **Github 地址**:[https://github.com/rzwitserloot/lombok](https://github.com/rzwitserloot/lombok)
|
||||
- **star**: 20.2 k
|
||||
- **介绍**: 对 Java 编程语言的非常刺激的补充。[https://projectlombok.org/](https://projectlombok.org/) 。
|
||||
|
||||
### 14.p3c
|
||||
|
||||
与我而言,没有特别惊艳,但是一些提供的一些代码规范确实挺有用的!
|
||||
|
||||
- **Github 地址**:[https://github.com/alibaba/p3c](https://github.com/alibaba/p3c)
|
||||
- **star**: 19.8 k
|
||||
- **介绍**: 阿里巴巴Java编码指南pmd实现和IDE插件。
|
||||
|
||||
### 15.spring-boot-demo
|
||||
|
||||
- **Github 地址**:[https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo)
|
||||
- **Star**: 8.8k
|
||||
- **介绍**: spring boot demo 是一个用来深度学习并实战 spring boot 的项目。
|
||||
|
||||
### 16. awesome-java
|
||||
|
||||
Guide 哥半个多月前开始维护的,虽然现在 Star 数量比较少,我相信后面一定会有更多人喜欢上这个项目,我也会继续认真维护下去。
|
||||
|
||||
- **Github 地址**:[https://github.com/Snailclimb/awesome-java](https://github.com/Snailclimb/awesome-java)
|
||||
- **Star**: 0.3 k
|
||||
- **介绍**: Github 上非常棒的 Java 开源项目集合。
|
||||
|
||||
|
||||
|
@ -1,64 +0,0 @@
|
||||
### 1. JavaGuide
|
||||
|
||||
- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
|
||||
- **Star**: 27.2k (4,437 stars this month)
|
||||
- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
|
||||
|
||||
### 2.DoraemonKit
|
||||
|
||||
- **Github地址**: <https://github.com/didi/DoraemonKit>
|
||||
- **Star**: 5.2k (3,786 stars this month)
|
||||
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
|
||||
|
||||
### 3.advanced-java
|
||||
|
||||
- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**:11.2k (3,042 stars this month)
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
|
||||
|
||||
### 4. spring-boot-examples
|
||||
|
||||
- **Github地址**:<https://github.com/ityouknow/spring-boot-examples>
|
||||
- **star**: 9.6 k (1,764 stars this month)
|
||||
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
|
||||
|
||||
### 5. mall
|
||||
|
||||
- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 7.4 k (1,736 stars this month)
|
||||
- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 6. fescar
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar)
|
||||
- **star**: 6.0 k (1,308 stars this month)
|
||||
- **介绍**: 具有 **高性能** 和 **易用性** 的 **微服务架构** 的 **分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。)
|
||||
|
||||
### 7. h4cker
|
||||
|
||||
- **Github地址**:<https://github.com/The-Art-of-Hacking/h4cker>
|
||||
- **star**: 2.1 k (1,303 stars this month)
|
||||
- **介绍**: 该仓库主要由Omar Santos维护,包括与道德黑客/渗透测试,数字取证和事件响应(DFIR),漏洞研究,漏洞利用开发,逆向工程等相关的资源。
|
||||
|
||||
### 8. spring-boot
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 34.8k (1,073 stars this month)
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
**关于Spring Boot官方的介绍:**
|
||||
|
||||
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
|
||||
|
||||
### 9. arthas
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 10.5 k (970 stars this month)
|
||||
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
|
||||
|
||||
### 10. tutorials
|
||||
|
||||
- **Github地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
|
||||
- **star**: 12.1 k (789 stars this month)
|
||||
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。
|
||||
|
@ -1,60 +0,0 @@
|
||||
### 1. JavaGuide
|
||||
|
||||
- **Github 地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
|
||||
- **Star**: 32.9k (6,196 stars this month)
|
||||
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
|
||||
|
||||
### 2.advanced-java
|
||||
|
||||
- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**: 15.1k (4,012 stars this month)
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
|
||||
|
||||
### 3.spring-boot-examples
|
||||
|
||||
- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
|
||||
- **Star**: 12.8k (3,462 stars this month)
|
||||
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
|
||||
|
||||
### 4. mall
|
||||
|
||||
- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 9.7 k (2,418 stars this month)
|
||||
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 5. seata
|
||||
|
||||
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
|
||||
- **star**: 7.2 k (1359 stars this month)
|
||||
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
|
||||
|
||||
### 6. quarkus
|
||||
|
||||
- **Github 地址**:[https://github.com/quarkusio/quarkus](https://github.com/quarkusio/quarkus)
|
||||
- **star**: 12 k (1,224 stars this month)
|
||||
- **介绍**: Quarkus 是为 GraalVM 和 HotSpot 量身定制的 Kubernetes Native Java 框架,由最佳的 Java 库和标准精心打造而成。Quarkus 的目标是使 Java 成为 Kubernetes 和无服务器环境中的领先平台,同时为开发人员提供统一的反应式和命令式编程模型,以优化地满足更广泛的分布式应用程序架构。
|
||||
|
||||
### 7. arthas
|
||||
|
||||
- **Github 地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 11.6 k (1,199 stars this month)
|
||||
- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。
|
||||
|
||||
### 8.DoraemonKit
|
||||
|
||||
- **Github 地址**: <https://github.com/didi/DoraemonKit>
|
||||
- **Star**: 6.2k (1,177 stars this month)
|
||||
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
|
||||
|
||||
### 9.elasticsearch
|
||||
|
||||
- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
|
||||
- **Star**: 39.7k (1,069 stars this month)
|
||||
- **介绍**: 开源,分布式,RESTful 搜索引擎。
|
||||
|
||||
### 10. tutorials
|
||||
|
||||
- **Github 地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
|
||||
- **star**: 13 k (998 stars this month)
|
||||
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。
|
||||
|
@ -1,98 +0,0 @@
|
||||
以下涉及到的数据统计与 2019 年 5 月 1 日 12 点,数据来源:<https://github.com/trending/java?since=monthly> 。
|
||||
|
||||
下面的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,比如下面推荐到的开源项目 Hutool 就是近期比较热门的项目之一,它是 Java 工具包,能够帮助我们简化代码!我觉得下面这些项目对于学习 Java 的朋友还是很有帮助的!
|
||||
|
||||
|
||||
### 1. JavaGuide
|
||||
|
||||
- **Github 地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
|
||||
- **Star**: 37.9k (5,660 stars this month)
|
||||
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
|
||||
|
||||
### 2. advanced-java
|
||||
|
||||
- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**: 15.1k (4,654 stars this month)
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
|
||||
|
||||
### 3. CS-Notes
|
||||
|
||||
- **Github 地址**:<https://github.com/CyC2018/CS-Notes>
|
||||
- **Star**: 59.2k (4,012 stars this month)
|
||||
- **介绍**: 技术面试必备基础知识。
|
||||
|
||||
### 4. ghidra
|
||||
|
||||
- **Github 地址**:<https://github.com/NationalSecurityAgency/ghidra>
|
||||
- **Star**: 15.0k (2,995 stars this month)
|
||||
- **介绍**: Ghidra是一个软件逆向工程(SRE)框架。
|
||||
|
||||
### 5. mall
|
||||
|
||||
- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 11.6 k (2,100 stars this month)
|
||||
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 6. ZXBlog
|
||||
|
||||
- **Github 地址**: <https://github.com/ZXZxin/ZXBlog>
|
||||
- **star**: 2.1 k (2,086 stars this month)
|
||||
- **介绍**: 记录各种学习笔记(算法、Java、数据库、并发......)。
|
||||
|
||||
### 7.DoraemonKit
|
||||
|
||||
- **Github地址**: <https://github.com/didi/DoraemonKit>
|
||||
- **Star**: 7.6k (1,541 stars this month)
|
||||
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
|
||||
|
||||
### 8. spring-boot
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 37.3k (1,489 stars this month)
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
**Spring Boot官方的介绍:**
|
||||
|
||||
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
|
||||
|
||||
### 9. spring-boot-examples
|
||||
|
||||
- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
|
||||
- **Star**: 12.8k (1,453 stars this month)
|
||||
- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。
|
||||
|
||||
### 10. seata
|
||||
|
||||
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
|
||||
- **star**: 8.4 k (1441 stars this month)
|
||||
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
|
||||
|
||||
### 11. litemall
|
||||
|
||||
- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples)
|
||||
- **Star**: 6.0k (1,427 stars this month)
|
||||
- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。
|
||||
|
||||
### 12. skywalking
|
||||
|
||||
- **Github 地址**:<https://github.com/apache/skywalking>
|
||||
- **Star**: 8.0k (1,381 stars this month)
|
||||
- **介绍**: 针对分布式系统的应用性能监控,尤其是针对微服务、云原生和面向容器的分布式系统架构。
|
||||
|
||||
### 13. elasticsearch
|
||||
|
||||
- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
|
||||
- **Star**: 4.0k (1,068stars this month)
|
||||
- **介绍**: 开源,分布式,RESTful 搜索引擎。
|
||||
|
||||
### 14. arthas
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 12.6 k (1,080 stars this month)
|
||||
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
|
||||
|
||||
### 15. hutool
|
||||
|
||||
- **Github地址**:<https://github.com/looly/hutool>
|
||||
- **star**: 4.5 k (1,031 stars this month)
|
||||
- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网:<https://www.hutool.cn/> 。
|
@ -1,125 +0,0 @@
|
||||
以下涉及到的数据统计与 2019 年 6 月 1 日 18 点,数据来源:<https://github.com/trending/java?since=monthly> 。下面推荐的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,建议收藏+在看!
|
||||
|
||||
### 1.LeetCodeAnimation
|
||||
|
||||
- **Github 地址**: <https://github.com/MisterBooo/LeetCodeAnimation>
|
||||
- **Star**: 29.0k (11,492 stars this month)
|
||||
- **介绍**: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。
|
||||
|
||||
### 2.CS-Notes
|
||||
|
||||
- **Github 地址**:<https://github.com/CyC2018/CS-Notes>
|
||||
- **Star**: 64.4k (5513 stars this month)
|
||||
- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。
|
||||
|
||||
### 3.JavaGuide
|
||||
|
||||
- **Github 地址**:<https://github.com/Snailclimb/JavaGuide>
|
||||
- **Star**: 42.0k (4,442 stars this month)
|
||||
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
|
||||
|
||||
### 4.mall
|
||||
|
||||
- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall)
|
||||
- **star**: 14.6 k (3,086 stars this month)
|
||||
- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
|
||||
|
||||
### 5.advanced-java
|
||||
|
||||
- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**: 20.8k (2,394 stars this month)
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。
|
||||
|
||||
### 6.spring-boot
|
||||
|
||||
- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot)
|
||||
- **star:** 38.5k (1,339 stars this month)
|
||||
- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。
|
||||
|
||||
**Spring Boot官方的介绍:**
|
||||
|
||||
> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可)
|
||||
|
||||
### 7. Java
|
||||
|
||||
- **Github 地址**:<https://github.com/TheAlgorithms/Java>
|
||||
- **Star**:14.3k (1,334 stars this month)
|
||||
- **介绍**: All Algorithms implemented in Java。
|
||||
|
||||
### 8.server
|
||||
|
||||
- **Github 地址**:<https://github.com/wildfirechat/server>
|
||||
- **star**: 2.2 k (1,275 stars this month)
|
||||
- **介绍**: 全开源即时通讯(IM)系统。
|
||||
|
||||
### 9.litemall
|
||||
|
||||
- **Github 地址**:<https://github.com/linlinjava/litemall>
|
||||
- **Star**: 7.1k (1,114 stars this month)
|
||||
- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。
|
||||
|
||||
### 10.Linkage-RecyclerView
|
||||
|
||||
- **Github 地址**:<https://github.com/KunMinX/Linkage-RecyclerView>
|
||||
- **Star**: 10.0k (1,093 stars this month)
|
||||
- **介绍**: 即使不用饿了么订餐,也请务必收藏好该库!🔥 一行代码即可接入,二级联动订餐列表 - Even if you don't order food by PrubHub, be sure to collect this library, please! 🔥 This secondary linkage list widget can be accessed by only one line of code. Supporting by RecyclerView & AndroidX.
|
||||
|
||||
### 11.toBeTopJavaer
|
||||
|
||||
- **Github 地址** : <https://github.com/hollischuang/toBeTopJavaer>
|
||||
- **Star**: 3.3k (1,007 stars this month)
|
||||
- **介绍**: To Be Top Javaer - Java工程师成神之路
|
||||
|
||||
### 12.elasticsearch
|
||||
|
||||
- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
|
||||
- **Star**: 48.0k (968 stars this month)
|
||||
- **介绍**: 开源,分布式,RESTful 搜索引擎。
|
||||
|
||||
### 13.java-design-patterns
|
||||
|
||||
- **Github 地址** : <https://github.com/iluwatar/java-design-patterns>
|
||||
- **Star**: 41.5k (955 stars this month)
|
||||
- **介绍**: Design patterns implemented in Java。
|
||||
|
||||
### 14.apollo
|
||||
|
||||
- **Github 地址** : <https://github.com/ctripcorp/apollo>
|
||||
- **Star**: 14.5k (927 stars this month)
|
||||
- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
|
||||
|
||||
### 15.arthas
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas)
|
||||
- **star**: 13.5 k (933 stars this month)
|
||||
- **介绍**: Arthas 是Alibaba开源的Java诊断工具。
|
||||
|
||||
### 16.dubbo
|
||||
|
||||
- **Github地址**:<https://github.com/apache/dubbo>
|
||||
- **star**: 26.9 k (769 stars this month)
|
||||
- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。
|
||||
|
||||
### 17.DoraemonKit
|
||||
|
||||
- **Github地址**: <https://github.com/didi/DoraemonKit>
|
||||
- **Star**: 8.5k (909 stars this month)
|
||||
- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。
|
||||
|
||||
### 18.halo
|
||||
|
||||
- **Github地址**: <https://github.com/halo-dev/halo>
|
||||
- **Star**: 4.1k (829 stars this month)
|
||||
- **介绍**: Halo 可能是最好的 Java 博客系统。
|
||||
|
||||
### 19.seata
|
||||
|
||||
- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata)
|
||||
- **star**: 9.2 k (776 stars this month)
|
||||
- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。
|
||||
|
||||
### 20.hutool
|
||||
|
||||
- **Github地址**:<https://github.com/looly/hutool>
|
||||
- **star**: 5,3 k (812 stars this month)
|
||||
- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网:<https://www.hutool.cn/> 。
|
@ -1,119 +0,0 @@
|
||||
### 1.CS-Notes
|
||||
|
||||
- **Github 地址**:https://github.com/CyC2018/CS-Notes
|
||||
- **Star**: 69.8k
|
||||
- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。
|
||||
|
||||
### 2.toBeTopJavaer
|
||||
|
||||
- **Github 地址:**[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer)
|
||||
- **Star**: 4.7k
|
||||
- **介绍**: To Be Top Javaer - Java工程师成神之路。
|
||||
|
||||
### 3.p3c
|
||||
|
||||
- **Github 地址:** [https://github.com/alibaba/p3c](https://github.com/alibaba/p3c)
|
||||
- **Star**: 16.6k
|
||||
- **介绍**: Alibaba Java Coding Guidelines pmd implements and IDE plugin。Eclipse 和 IDEA 上都有该插件,推荐使用!
|
||||
|
||||
### 4.SpringCloudLearning
|
||||
|
||||
- **Github 地址:** [https://github.com/forezp/SpringCloudLearning](https://github.com/forezp/SpringCloudLearning)
|
||||
- **Star**: 8.7k
|
||||
- **介绍**: 史上最简单的Spring Cloud教程源码。
|
||||
|
||||
### 5.dubbo
|
||||
|
||||
- **Github地址**:<https://github.com/apache/dubbo>
|
||||
- **star**: 27.6 k
|
||||
- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。
|
||||
|
||||
### 6.jeecg-boot
|
||||
|
||||
- **Github地址**: [https://github.com/zhangdaiscott/jeecg-boot](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
- **star**: 3.3 k
|
||||
- **介绍**: 一款基于代码生成器的JAVA快速开发平台!全新架构前后端分离:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。
|
||||
|
||||
### 7.advanced-java
|
||||
|
||||
- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
|
||||
- **Star**: 24.2k
|
||||
- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。
|
||||
|
||||
### 8.FEBS-Shiro
|
||||
|
||||
- **Github 地址**:[https://github.com/wuyouzhuguli/FEBS-Shiro](https://github.com/wuyouzhuguli/FEBS-Shiro)
|
||||
- **Star**: 2.6k
|
||||
- **介绍**: Spring Boot 2.1.3,Shiro1.4.0 & Layui 2.5.4 权限管理系统。预览地址:http://49.234.20.223:8080/login。
|
||||
|
||||
### 9.SpringAll
|
||||
|
||||
- **Github 地址**: [https://github.com/wuyouzhuguli/SpringAll](https://github.com/wuyouzhuguli/SpringAll)
|
||||
- **Star**: 5.4k
|
||||
- **介绍**: 循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码。
|
||||
|
||||
### 10.JavaGuide
|
||||
|
||||
- **Github 地址**:<https://github.com/Snailclimb/JavaGuide>
|
||||
- **Star**: 47.2k
|
||||
- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。
|
||||
|
||||
### 11.vhr
|
||||
|
||||
- **Github 地址**:[https://github.com/lenve/vhr](https://github.com/lenve/vhr)
|
||||
- **Star**: 4.9k
|
||||
- **介绍**: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。
|
||||
|
||||
### 12. tutorials
|
||||
|
||||
- **Github 地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials)
|
||||
- **star**: 15.4 k
|
||||
- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。
|
||||
|
||||
### 13.EasyScheduler
|
||||
|
||||
- **Github 地址**:[https://github.com/analysys/EasyScheduler](https://github.com/analysys/EasyScheduler)
|
||||
- **star**: 1.1 k
|
||||
- **介绍**: Easy Scheduler是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler以DAG方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。https://analysys.github.io/easyscheduler_docs_cn/
|
||||
|
||||
### 14.thingsboard
|
||||
|
||||
- **Github 地址**:[https://github.com/thingsboard/thingsboard](https://github.com/thingsboard/thingsboard)
|
||||
- **star**: 3.7 k
|
||||
- **介绍**: 开源物联网平台 - 设备管理,数据收集,处理和可视化。 [https://thingsboard.io](https://thingsboard.io/)
|
||||
|
||||
### 15.mall-learning
|
||||
|
||||
- **Github 地址**: [https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning)
|
||||
- **star**: 0.6 k
|
||||
- **介绍**: mall学习教程,架构、业务、技术要点全方位解析。mall项目(16k+star)是一套电商系统,使用现阶段主流技术实现。 涵盖了SpringBoot2.1.3、MyBatis3.4.6、Elasticsearch6.2.2、RabbitMQ3.7.15、Redis3.2、Mongodb3.2、Mysql5.7等技术,采用Docker容器化部署。 https://github.com/macrozheng/mall
|
||||
|
||||
### 16. flink
|
||||
|
||||
- **Github地址**:[https://github.com/apache/flink](https://github.com/apache/flink)
|
||||
- **star**: 9.3 k
|
||||
- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。
|
||||
|
||||
### 17.spring-cloud-kubernetes
|
||||
|
||||
- **Github地址**:[https://github.com/spring-cloud/spring-cloud-kubernetes](https://github.com/spring-cloud/spring-cloud-kubernetes)
|
||||
- **star**: 1.4 k
|
||||
- **介绍**: Kubernetes 集成 Spring Cloud Discovery Client, Configuration, etc...
|
||||
|
||||
### 18.springboot-learning-example
|
||||
|
||||
- **Github地址**:[https://github.com/JeffLi1993/springboot-learning-example](https://github.com/JeffLi1993/springboot-learning-example)
|
||||
- **star**: 10.0 k
|
||||
- **介绍**: spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
|
||||
|
||||
### 19.canal
|
||||
|
||||
- **Github地址**:[https://github.com/alibaba/canal](https://github.com/alibaba/canal)
|
||||
- **star**: 9.3 k
|
||||
- **介绍**: 阿里巴巴 MySQL binlog 增量订阅&消费组件。
|
||||
|
||||
### 20.react-native-device-info
|
||||
|
||||
- **Github地址**:[https://github.com/react-native-community/react-native-device-info](https://github.com/react-native-community/react-native-device-info)
|
||||
- **star**: 4.0 k
|
||||
- **介绍**: React Native iOS和Android的设备信息。
|
@ -1,8 +0,0 @@
|
||||
- [2018 年 12 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2018-12.md)
|
||||
- [2019 年 1 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-1.md)
|
||||
- [2019 年 2 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-2.md)
|
||||
- [2019 年 3 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-3.md)
|
||||
- [2019 年 4 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-4.md)
|
||||
- [2019 年 5 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-5.md)
|
||||
- [2019 年 6 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-6.md)
|
||||
|
@ -1,200 +0,0 @@
|
||||
<!-- MarkdownTOC -->
|
||||
|
||||
- [IO流学习总结](#io流学习总结)
|
||||
- [一 Java IO,硬骨头也能变软](#一-java-io,硬骨头也能变软)
|
||||
- [二 java IO体系的学习总结](#二-java-io体系的学习总结)
|
||||
- [三 Java IO面试题](#三-java-io面试题)
|
||||
- [NIO与AIO学习总结](#nio与aio学习总结)
|
||||
- [一 Java NIO 概览](#一-java-nio-概览)
|
||||
- [二 Java NIO 之 Buffer\(缓冲区\)](#二-java-nio-之-buffer缓冲区)
|
||||
- [三 Java NIO 之 Channel(通道)](#三-java-nio-之-channel(通道))
|
||||
- [四 Java NIO之Selector(选择器)](#四-java-nio之selector(选择器))
|
||||
- [五 Java NIO之拥抱Path和Files](#五-java-nio之拥抱path和files)
|
||||
- [六 NIO学习总结以及NIO新特性介绍](#六-nio学习总结以及nio新特性介绍)
|
||||
- [七 Java NIO AsynchronousFileChannel异步文件通](#七-java-nio-asynchronousfilechannel异步文件通)
|
||||
- [八 高并发Java(8):NIO和AIO](#八-高并发java(8):nio和aio)
|
||||
- [推荐阅读](#推荐阅读)
|
||||
- [在 Java 7 中体会 NIO.2 异步执行的快乐](#在-java-7-中体会-nio2-异步执行的快乐)
|
||||
- [Java AIO总结与示例](#java-aio总结与示例)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
|
||||
|
||||
## IO流学习总结
|
||||
|
||||
### [一 Java IO,硬骨头也能变软](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483981&idx=1&sn=6e5c682d76972c8d2cf271a85dcf09e2&chksm=fd98542ccaefdd3a70428e9549bc33e8165836855edaa748928d16c1ebde9648579d3acaac10#rd)
|
||||
|
||||
**(1) 按操作方式分类结构图:**
|
||||
|
||||

|
||||
|
||||
|
||||
**(2)按操作对象分类结构图**
|
||||
|
||||

|
||||
|
||||
### [二 java IO体系的学习总结](https://blog.csdn.net/nightcurtis/article/details/51324105)
|
||||
1. **IO流的分类:**
|
||||
- 按照流的流向分,可以分为输入流和输出流;
|
||||
- 按照操作单元划分,可以划分为字节流和字符流;
|
||||
- 按照流的角色划分为节点流和处理流。
|
||||
2. **流的原理浅析:**
|
||||
|
||||
java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。
|
||||
|
||||
- **InputStream/Reader**: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
|
||||
- **OutputStream/Writer**: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
|
||||
3. **常用的io流的用法**
|
||||
|
||||
### [三 Java IO面试题](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483985&idx=1&sn=38531c2cee7b87f125df7aef41637014&chksm=fd985430caefdd26b0506aa84fc26251877eccba24fac73169a4d6bd1eb5e3fbdf3c3b940261#rd)
|
||||
|
||||
## NIO与AIO学习总结
|
||||
|
||||
|
||||
### [一 Java NIO 概览](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483956&idx=1&sn=57692bc5b7c2c6dfb812489baadc29c9&chksm=fd985455caefdd4331d828d8e89b22f19b304aa87d6da73c5d8c66fcef16e4c0b448b1a6f791#rd)
|
||||
|
||||
1. **NIO简介**:
|
||||
|
||||
Java NIO 是 java 1.4, 之后新出的一套IO接口NIO中的N可以理解为Non-blocking,不单纯是New。
|
||||
|
||||
2. **NIO的特性/NIO与IO区别:**
|
||||
- 1)IO是面向流的,NIO是面向缓冲区的;
|
||||
- 2)IO流是阻塞的,NIO流是不阻塞的;
|
||||
- 3)NIO有选择器,而IO没有。
|
||||
3. **读数据和写数据方式:**
|
||||
- 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
|
||||
|
||||
- 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。
|
||||
|
||||
4. **NIO核心组件简单介绍**
|
||||
- **Channels**
|
||||
- **Buffers**
|
||||
- **Selectors**
|
||||
|
||||
|
||||
### [二 Java NIO 之 Buffer(缓冲区)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483961&idx=1&sn=f67bef4c279e78043ff649b6b03fdcbc&chksm=fd985458caefdd4e3317ccbdb2d0a5a70a5024d3255eebf38183919ed9c25ade536017c0a6ba#rd)
|
||||
|
||||
1. **Buffer(缓冲区)介绍:**
|
||||
- Java NIO Buffers用于和NIO Channel交互。 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels;
|
||||
- Buffer本质上就是一块内存区;
|
||||
- 一个Buffer有三个属性是必须掌握的,分别是:capacity容量、position位置、limit限制。
|
||||
2. **Buffer的常见方法**
|
||||
- Buffer clear()
|
||||
- Buffer flip()
|
||||
- Buffer rewind()
|
||||
- Buffer position(int newPosition)
|
||||
3. **Buffer的使用方式/方法介绍:**
|
||||
- 分配缓冲区(Allocating a Buffer):
|
||||
```java
|
||||
ByteBuffer buf = ByteBuffer.allocate(28);//以ByteBuffer为例子
|
||||
```
|
||||
- 写入数据到缓冲区(Writing Data to a Buffer)
|
||||
|
||||
**写数据到Buffer有两种方法:**
|
||||
|
||||
1.从Channel中写数据到Buffer
|
||||
```java
|
||||
int bytesRead = inChannel.read(buf); //read into buffer.
|
||||
```
|
||||
2.通过put写数据:
|
||||
```java
|
||||
buf.put(127);
|
||||
```
|
||||
|
||||
4. **Buffer常用方法测试**
|
||||
|
||||
说实话,NIO编程真的难,通过后面这个测试例子,你可能才能勉强理解前面说的Buffer方法的作用。
|
||||
|
||||
|
||||
### [三 Java NIO 之 Channel(通道)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483966&idx=1&sn=d5cf18c69f5f9ec2aff149270422731f&chksm=fd98545fcaefdd49296e2c78000ce5da277435b90ba3c03b92b7cf54c6ccc71d61d13efbce63#rd)
|
||||
|
||||
|
||||
1. **Channel(通道)介绍**
|
||||
- 通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
|
||||
- NIO Channel通道和流的区别:
|
||||
2. **FileChannel的使用**
|
||||
3. **SocketChannel和ServerSocketChannel的使用**
|
||||
4. **️DatagramChannel的使用**
|
||||
5. **Scatter / Gather**
|
||||
- Scatter: 从一个Channel读取的信息分散到N个缓冲区中(Buufer).
|
||||
- Gather: 将N个Buffer里面内容按照顺序发送到一个Channel.
|
||||
6. **通道之间的数据传输**
|
||||
- 在Java NIO中如果一个channel是FileChannel类型的,那么他可以直接把数据传输到另一个channel。
|
||||
- transferFrom() :transferFrom方法把数据从通道源传输到FileChannel
|
||||
- transferTo() :transferTo方法把FileChannel数据传输到另一个channel
|
||||
|
||||
|
||||
### [四 Java NIO之Selector(选择器)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483970&idx=1&sn=d5e2b133313b1d0f32872d54fbdf0aa7&chksm=fd985423caefdd354b587e57ce6cf5f5a7bec48b9ab7554f39a8d13af47660cae793956e0f46#rd)
|
||||
|
||||
|
||||
1. **Selector(选择器)介绍**
|
||||
- Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
|
||||
- 使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。
|
||||
2. **Selector(选择器)的使用方法介绍**
|
||||
- Selector的创建
|
||||
```java
|
||||
Selector selector = Selector.open();
|
||||
```
|
||||
- 注册Channel到Selector(Channel必须是非阻塞的)
|
||||
```java
|
||||
channel.configureBlocking(false);
|
||||
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
|
||||
```
|
||||
- SelectionKey介绍
|
||||
|
||||
一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
|
||||
- 从Selector中选择channel(Selecting Channels via a Selector)
|
||||
|
||||
选择器维护注册过的通道的集合,并且这种注册关系都被封装在SelectionKey当中.
|
||||
- 停止选择的方法
|
||||
|
||||
wakeup()方法 和close()方法。
|
||||
3. **模板代码**
|
||||
|
||||
有了模板代码我们在编写程序时,大多数时间都是在模板代码中添加相应的业务代码。
|
||||
4. **客户端与服务端简单交互实例**
|
||||
|
||||
|
||||
|
||||
### [五 Java NIO之拥抱Path和Files](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483976&idx=1&sn=2296c05fc1b840a64679e2ad7794c96d&chksm=fd985429caefdd3f48e2ee6fdd7b0f6fc419df90b3de46832b484d6d1ca4e74e7837689c8146&token=537240785&lang=zh_CN#rd)
|
||||
|
||||
**一 文件I/O基石:Path:**
|
||||
- 创建一个Path
|
||||
- File和Path之间的转换,File和URI之间的转换
|
||||
- 获取Path的相关信息
|
||||
- 移除Path中的冗余项
|
||||
|
||||
**二 拥抱Files类:**
|
||||
- Files.exists() 检测文件路径是否存在
|
||||
- Files.createFile() 创建文件
|
||||
- Files.createDirectories()和Files.createDirectory()创建文件夹
|
||||
- Files.delete()方法 可以删除一个文件或目录
|
||||
- Files.copy()方法可以吧一个文件从一个地址复制到另一个位置
|
||||
- 获取文件属性
|
||||
- 遍历一个文件夹
|
||||
- Files.walkFileTree()遍历整个目录
|
||||
|
||||
### [六 NIO学习总结以及NIO新特性介绍](https://blog.csdn.net/a953713428/article/details/64907250)
|
||||
|
||||
- **内存映射:**
|
||||
|
||||
这个功能主要是为了提高大文件的读写速度而设计的。内存映射文件(memory-mappedfile)能让你创建和修改那些大到无法读入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问了。将文件的一段区域映射到内存中,比传统的文件处理速度要快很多。内存映射文件它虽然最终也是要从磁盘读取数据,但是它并不需要将数据读取到OS内核缓冲区,而是直接将进程的用户私有地址空间中的一部分区域与文件对象建立起映射关系,就好像直接从内存中读、写文件一样,速度当然快了。
|
||||
|
||||
### [七 Java NIO AsynchronousFileChannel异步文件通](http://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-asynchronousfilechannel.html)
|
||||
|
||||
Java7中新增了AsynchronousFileChannel作为nio的一部分。AsynchronousFileChannel使得数据可以进行异步读写。
|
||||
|
||||
### [八 高并发Java(8):NIO和AIO](http://www.importnew.com/21341.html)
|
||||
|
||||
|
||||
|
||||
## 推荐阅读
|
||||
|
||||
### [在 Java 7 中体会 NIO.2 异步执行的快乐](https://www.ibm.com/developerworks/cn/java/j-lo-nio2/index.html)
|
||||
|
||||
### [Java AIO总结与示例](https://blog.csdn.net/x_i_y_u_e/article/details/52223406)
|
||||
AIO是异步IO的缩写,虽然NIO在网络操作中,提供了非阻塞的方法,但是NIO的IO行为还是同步的。对于NIO来说,我们的业务线程是在IO操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。
|
||||
|
||||
|
||||
**欢迎关注我的微信公众号:"Java面试通关手册"(一个有温度的微信公众号,期待与你共同进步~~~坚持原创,分享美文,分享各种Java学习资源):**
|
@ -1,422 +0,0 @@
|
||||
## synchronized / Lock
|
||||
|
||||
1. JDK 1.5之前
|
||||
|
||||
,Java通过
|
||||
|
||||
synchronized
|
||||
|
||||
关键字来实现
|
||||
|
||||
锁
|
||||
|
||||
功能
|
||||
|
||||
- synchronized是JVM实现的**内置锁**,锁的获取和释放都是由JVM**隐式**实现的
|
||||
|
||||
2. JDK 1.5
|
||||
|
||||
,并发包中新增了
|
||||
|
||||
Lock接口
|
||||
|
||||
来实现锁功能
|
||||
|
||||
- 提供了与synchronized类似的同步功能,但需要**显式**获取和释放锁
|
||||
|
||||
3. Lock同步锁是基于
|
||||
|
||||
Java
|
||||
|
||||
实现的,而synchronized是基于底层操作系统的
|
||||
|
||||
Mutex Lock
|
||||
|
||||
实现的
|
||||
|
||||
- 每次获取和释放锁都会带来**用户态和内核态的切换**,从而增加系统的**性能开销**
|
||||
- 在锁竞争激烈的情况下,synchronized同步锁的性能很糟糕
|
||||
- 在**JDK 1.5**,在**单线程重复申请锁**的情况下,synchronized锁性能要比Lock的性能**差很多**
|
||||
|
||||
4. **JDK 1.6**,Java对synchronized同步锁做了**充分的优化**,甚至在某些场景下,它的性能已经超越了Lock同步锁
|
||||
|
||||
|
||||
|
||||
## 实现原理
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
public class SyncTest {
|
||||
public synchronized void method1() {
|
||||
}
|
||||
|
||||
public void method2() {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
$ javac -encoding UTF-8 SyncTest.java
|
||||
$ javap -v SyncTest
|
||||
```
|
||||
|
||||
### 修饰方法
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
public synchronized void method1();
|
||||
descriptor: ()V
|
||||
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
|
||||
Code:
|
||||
stack=0, locals=1, args_size=1
|
||||
0: return
|
||||
```
|
||||
|
||||
1. JVM使用**ACC_SYNCHRONIZED**访问标识来区分一个方法是否为**同步方法**
|
||||
|
||||
2. 在方法调用时,会检查方法是否被设置了
|
||||
|
||||
ACC_SYNCHRONIZED
|
||||
|
||||
访问标识
|
||||
|
||||
- 如果是,执行线程会将先尝试**持有Monitor对象**,再执行方法,方法执行完成后,最后**释放Monitor对象**
|
||||
|
||||
### 修饰代码块
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
public void method2();
|
||||
descriptor: ()V
|
||||
flags: ACC_PUBLIC
|
||||
Code:
|
||||
stack=2, locals=4, args_size=1
|
||||
0: new #2 // class java/lang/Object
|
||||
3: dup
|
||||
4: invokespecial #1 // Method java/lang/Object."<init>":()V
|
||||
7: astore_1
|
||||
8: aload_1
|
||||
9: dup
|
||||
10: astore_2
|
||||
11: monitorenter
|
||||
12: aload_2
|
||||
13: monitorexit
|
||||
14: goto 22
|
||||
17: astore_3
|
||||
18: aload_2
|
||||
19: monitorexit
|
||||
20: aload_3
|
||||
21: athrow
|
||||
22: return
|
||||
```
|
||||
|
||||
1. synchronized修饰同步代码块时,由**monitorenter**和**monitorexit**指令来实现同步
|
||||
2. 进入**monitorenter**指令后,线程将**持有**该**Monitor对象**,进入**monitorexit**指令,线程将**释放**该**Monitor对象**
|
||||
|
||||
### 管程模型
|
||||
|
||||
1. JVM中的**同步**是基于进入和退出**管程**(**Monitor**)对象实现的
|
||||
|
||||
2. **每个Java对象实例都会有一个Monitor**,Monitor可以和Java对象实例一起被创建和销毁
|
||||
|
||||
3. Monitor是由**ObjectMonitor**实现的,对应[ObjectMonitor.hpp](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/objectMonitor.hpp)
|
||||
|
||||
4. 当多个线程同时访问一段同步代码时,会先被放在**EntryList**中
|
||||
|
||||
5. 当线程获取到Java对象的Monitor时(Monitor是依靠
|
||||
|
||||
底层操作系统
|
||||
|
||||
的
|
||||
|
||||
Mutex Lock
|
||||
|
||||
来实现
|
||||
|
||||
互斥
|
||||
|
||||
的)
|
||||
|
||||
- 线程申请Mutex成功,则持有该Mutex,其它线程将无法获取到该Mutex
|
||||
|
||||
6. 进入
|
||||
|
||||
WaitSet
|
||||
|
||||
- 竞争锁**失败**的线程会进入**WaitSet**
|
||||
- 竞争锁**成功**的线程如果调用**wait**方法,就会**释放当前持有的Mutex**,并且该线程会进入**WaitSet**
|
||||
- 进入**WaitSet**的进程会等待下一次唤醒,然后进入EntryList**重新排队**
|
||||
|
||||
7. 如果当前线程顺利执行完方法,也会释放Mutex
|
||||
|
||||
8. Monitor依赖于**底层操作系统**的实现,存在**用户态**和**内核态之间**的**切换**,所以增加了**性能开销**
|
||||
|
||||
[](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-monitor.png)
|
||||
|
||||
复制
|
||||
|
||||
```
|
||||
ObjectMonitor() {
|
||||
_header = NULL;
|
||||
_count = 0; // 记录个数
|
||||
_waiters = 0,
|
||||
_recursions = 0;
|
||||
_object = NULL;
|
||||
_owner = NULL; // 持有该Monitor的线程
|
||||
_WaitSet = NULL; // 处于wait状态的线程,会被加入 _WaitSet
|
||||
_WaitSetLock = 0 ;
|
||||
_Responsible = NULL ;
|
||||
_succ = NULL ;
|
||||
_cxq = NULL ;
|
||||
FreeNext = NULL ;
|
||||
_EntryList = NULL ; // 多个线程访问同步块或同步方法,会首先被加入 _EntryList
|
||||
_SpinFreq = 0 ;
|
||||
_SpinClock = 0 ;
|
||||
OwnerIsThread = 0 ;
|
||||
_previous_owner_tid = 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 锁升级优化
|
||||
|
||||
1. 为了提升性能,在**JDK 1.6**引入**偏向锁、轻量级锁、重量级锁**,用来**减少锁竞争带来的上下文切换**
|
||||
2. 借助JDK 1.6新增的**Java对象头**,实现了**锁升级**功能
|
||||
|
||||
### Java对象头
|
||||
|
||||
1. 在**JDK 1.6**的JVM中,对象实例在**堆内存**中被分为三部分:**对象头**、**实例数据**、**对齐填充**
|
||||
2. 对象头的组成部分:**Mark Word**、**指向类的指针**、**数组长度**(可选,数组类型时才有)
|
||||
3. Mark Word记录了**对象**和**锁**有关的信息,在64位的JVM中,Mark Word为**64 bit**
|
||||
4. 锁升级功能主要依赖于Mark Word中**锁标志位**和**是否偏向锁标志位**
|
||||
5. synchronized同步锁的升级优化路径:***偏向锁** -> **轻量级锁** -> **重量级锁***
|
||||
|
||||
[](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-mark-word.jpg)
|
||||
|
||||
### 偏向锁
|
||||
|
||||
1. 偏向锁主要用来优化**同一线程多次申请同一个锁**的竞争,在某些情况下,大部分时间都是同一个线程竞争锁资源
|
||||
|
||||
2. 偏向锁的作用
|
||||
|
||||
- 当一个线程再次访问同一个同步代码时,该线程只需对该对象头的**Mark Word**中去判断是否有偏向锁指向它
|
||||
- **无需再进入Monitor去竞争对象**(避免用户态和内核态的**切换**)
|
||||
|
||||
3. 当对象被当做同步锁,并有一个线程抢到锁时
|
||||
|
||||
- 锁标志位还是**01**,是否偏向锁标志位设置为**1**,并且记录抢到锁的**线程ID**,进入***偏向锁状态***
|
||||
|
||||
4. 偏向锁
|
||||
|
||||
**不会主动释放锁**
|
||||
|
||||
- 当线程1再次获取锁时,会比较**当前线程的ID**与**锁对象头部的线程ID**是否一致,如果一致,无需CAS来抢占锁
|
||||
|
||||
- 如果不一致,需要查看
|
||||
|
||||
锁对象头部记录的线程
|
||||
|
||||
是否存活
|
||||
|
||||
- 如果**没有存活**,那么锁对象被重置为**无锁**状态(也是一种撤销),然后重新偏向线程2
|
||||
|
||||
- 如果
|
||||
|
||||
存活
|
||||
|
||||
,查找线程1的栈帧信息
|
||||
|
||||
- 如果线程1还是需要继续持有该锁对象,那么暂停线程1(**STW**),**撤销偏向锁**,**升级为轻量级锁**
|
||||
- 如果线程1不再使用该锁对象,那么将该锁对象设为**无锁**状态(也是一种撤销),然后重新偏向线程2
|
||||
|
||||
5. 一旦出现其他线程竞争锁资源时,偏向锁就会被
|
||||
|
||||
撤销
|
||||
|
||||
- 偏向锁的撤销**可能需要**等待**全局安全点**,暂停持有该锁的线程,同时检查该线程**是否还在执行该方法**
|
||||
- 如果还没有执行完,说明此刻有**多个线程**竞争,升级为**轻量级锁**;如果已经执行完毕,唤醒其他线程继续**CAS**抢占
|
||||
|
||||
6. 在
|
||||
|
||||
高并发
|
||||
|
||||
场景下,当
|
||||
|
||||
大量线程
|
||||
|
||||
同时竞争同一个锁资源时,偏向锁会被
|
||||
|
||||
撤销
|
||||
|
||||
,发生
|
||||
|
||||
STW
|
||||
|
||||
,加大了
|
||||
|
||||
性能开销
|
||||
|
||||
- 默认配置
|
||||
|
||||
- `-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=4000`
|
||||
- 默认开启偏向锁,并且**延迟生效**,因为JVM刚启动时竞争非常激烈
|
||||
|
||||
- 关闭偏向锁
|
||||
|
||||
- `-XX:-UseBiasedLocking`
|
||||
|
||||
- 直接
|
||||
|
||||
设置为重量级锁
|
||||
|
||||
- `-XX:+UseHeavyMonitors`
|
||||
|
||||
红线流程部分:偏向锁的**获取**和**撤销**
|
||||
[](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-1.png)
|
||||
|
||||
### 轻量级锁
|
||||
|
||||
1. 当有另外一个线程竞争锁时,由于该锁处于**偏向锁**状态
|
||||
|
||||
2. 发现对象头Mark Word中的线程ID不是自己的线程ID,该线程就会执行
|
||||
|
||||
CAS
|
||||
|
||||
操作获取锁
|
||||
|
||||
- 如果获取**成功**,直接替换Mark Word中的线程ID为自己的线程ID,该锁会***保持偏向锁状态***
|
||||
- 如果获取**失败**,说明当前锁有一定的竞争,将偏向锁**升级**为轻量级锁
|
||||
|
||||
3. 线程获取轻量级锁时会有两步
|
||||
|
||||
- 先把**锁对象的Mark Word**复制一份到线程的**栈帧**中(**DisplacedMarkWord**),主要为了**保留现场**!!
|
||||
- 然后使用**CAS**,把对象头中的内容替换为**线程栈帧中DisplacedMarkWord的地址**
|
||||
|
||||
4. 场景
|
||||
|
||||
- 在线程1复制对象头Mark Word的同时(CAS之前),线程2也准备获取锁,也复制了对象头Mark Word
|
||||
- 在线程2进行CAS时,发现线程1已经把对象头换了,线程2的CAS失败,线程2会尝试使用**自旋锁**来等待线程1释放锁
|
||||
|
||||
5. 轻量级锁的适用场景:线程**交替执行**同步块,***绝大部分的锁在整个同步周期内都不存在长时间的竞争***
|
||||
|
||||
红线流程部分:升级轻量级锁
|
||||
[](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-2.png)
|
||||
|
||||
### 自旋锁 / 重量级锁
|
||||
|
||||
1. 轻量级锁
|
||||
|
||||
CAS
|
||||
|
||||
抢占失败,线程将会被挂起进入
|
||||
|
||||
阻塞
|
||||
|
||||
状态
|
||||
|
||||
- 如果正在持有锁的线程在**很短的时间**内释放锁资源,那么进入**阻塞**状态的线程被**唤醒**后又要**重新抢占**锁资源
|
||||
|
||||
2. JVM提供了**自旋锁**,可以通过**自旋**的方式**不断尝试获取锁**,从而***避免线程被挂起阻塞***
|
||||
|
||||
3. 从
|
||||
|
||||
JDK 1.7
|
||||
|
||||
开始,
|
||||
|
||||
自旋锁默认启用
|
||||
|
||||
,自旋次数
|
||||
|
||||
不建议设置过大
|
||||
|
||||
(意味着
|
||||
|
||||
长时间占用CPU
|
||||
|
||||
)
|
||||
|
||||
- `-XX:+UseSpinning -XX:PreBlockSpin=10`
|
||||
|
||||
4. 自旋锁重试之后如果依然抢锁失败,同步锁会升级至
|
||||
|
||||
重量级锁
|
||||
|
||||
,锁标志位为
|
||||
|
||||
10
|
||||
|
||||
- 在这个状态下,未抢到锁的线程都会**进入Monitor**,之后会被阻塞在**WaitSet**中
|
||||
|
||||
5. 在
|
||||
|
||||
锁竞争不激烈
|
||||
|
||||
且
|
||||
|
||||
锁占用时间非常短
|
||||
|
||||
的场景下,自旋锁可以提高系统性能
|
||||
|
||||
- 一旦锁竞争激烈或者锁占用的时间过长,自旋锁将会导致大量的线程一直处于**CAS重试状态**,**占用CPU资源**
|
||||
|
||||
6. 在
|
||||
|
||||
高并发
|
||||
|
||||
的场景下,可以通过
|
||||
|
||||
关闭自旋锁
|
||||
|
||||
来优化系统性能
|
||||
|
||||
- ```
|
||||
-XX:-UseSpinning
|
||||
```
|
||||
|
||||
- 关闭自旋锁优化
|
||||
|
||||
- ```
|
||||
-XX:PreBlockSpin
|
||||
```
|
||||
|
||||
- 默认的自旋次数,在**JDK 1.7**后,**由JVM控制**
|
||||
|
||||
[](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-3.png)
|
||||
|
||||
## 小结
|
||||
|
||||
1. JVM在**JDK 1.6**中引入了**分级锁**机制来优化synchronized
|
||||
|
||||
2. 当一个线程获取锁时,首先对象锁成为一个
|
||||
|
||||
偏向锁
|
||||
|
||||
- 这是为了避免在**同一线程重复获取同一把锁**时,**用户态和内核态频繁切换**
|
||||
|
||||
3. 如果有多个线程竞争锁资源,锁将会升级为
|
||||
|
||||
轻量级锁
|
||||
|
||||
- 这适用于在**短时间**内持有锁,且分锁**交替切换**的场景
|
||||
- 轻量级锁还结合了**自旋锁**来**避免线程用户态与内核态的频繁切换**
|
||||
|
||||
4. 如果锁竞争太激烈(自旋锁失败),同步锁会升级为重量级锁
|
||||
|
||||
5. 优化synchronized同步锁的关键:
|
||||
|
||||
减少锁竞争
|
||||
|
||||
- 应该尽量使synchronized同步锁处于**轻量级锁**或**偏向锁**,这样才能提高synchronized同步锁的性能
|
||||
- 常用手段
|
||||
- **减少锁粒度**:降低锁竞争
|
||||
- **减少锁的持有时间**,提高synchronized同步锁在自旋时获取锁资源的成功率,**避免升级为重量级锁**
|
||||
|
||||
6. 在**锁竞争激烈**时,可以考虑**禁用偏向锁**和**禁用自旋锁**
|
@ -1,383 +0,0 @@
|
||||
<!-- TOC -->
|
||||
|
||||
- [Collections 工具类和 Arrays 工具类常见方法](#collections-工具类和-arrays-工具类常见方法)
|
||||
- [Collections](#collections)
|
||||
- [排序操作](#排序操作)
|
||||
- [查找,替换操作](#查找替换操作)
|
||||
- [同步控制](#同步控制)
|
||||
- [Arrays类的常见操作](#arrays类的常见操作)
|
||||
- [排序 : `sort()`](#排序--sort)
|
||||
- [查找 : `binarySearch()`](#查找--binarysearch)
|
||||
- [比较: `equals()`](#比较-equals)
|
||||
- [填充 : `fill()`](#填充--fill)
|
||||
- [转列表 `asList()`](#转列表-aslist)
|
||||
- [转字符串 `toString()`](#转字符串-tostring)
|
||||
- [复制 `copyOf()`](#复制-copyof)
|
||||
|
||||
<!-- /TOC -->
|
||||
# Collections 工具类和 Arrays 工具类常见方法
|
||||
|
||||
## Collections
|
||||
|
||||
Collections 工具类常用方法:
|
||||
|
||||
1. 排序
|
||||
2. 查找,替换操作
|
||||
3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合)
|
||||
|
||||
### 排序操作
|
||||
|
||||
```java
|
||||
void reverse(List list)//反转
|
||||
void shuffle(List list)//随机排序
|
||||
void sort(List list)//按自然排序的升序排序
|
||||
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
|
||||
void swap(List list, int i , int j)//交换两个索引位置的元素
|
||||
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。
|
||||
```
|
||||
|
||||
**示例代码:**
|
||||
|
||||
```java
|
||||
ArrayList<Integer> arrayList = new ArrayList<Integer>();
|
||||
arrayList.add(-1);
|
||||
arrayList.add(3);
|
||||
arrayList.add(3);
|
||||
arrayList.add(-5);
|
||||
arrayList.add(7);
|
||||
arrayList.add(4);
|
||||
arrayList.add(-9);
|
||||
arrayList.add(-7);
|
||||
System.out.println("原始数组:");
|
||||
System.out.println(arrayList);
|
||||
// void reverse(List list):反转
|
||||
Collections.reverse(arrayList);
|
||||
System.out.println("Collections.reverse(arrayList):");
|
||||
System.out.println(arrayList);
|
||||
|
||||
|
||||
Collections.rotate(arrayList, 4);
|
||||
System.out.println("Collections.rotate(arrayList, 4):");
|
||||
System.out.println(arrayList);
|
||||
|
||||
// void sort(List list),按自然排序的升序排序
|
||||
Collections.sort(arrayList);
|
||||
System.out.println("Collections.sort(arrayList):");
|
||||
System.out.println(arrayList);
|
||||
|
||||
// void shuffle(List list),随机排序
|
||||
Collections.shuffle(arrayList);
|
||||
System.out.println("Collections.shuffle(arrayList):");
|
||||
System.out.println(arrayList);
|
||||
|
||||
// void swap(List list, int i , int j),交换两个索引位置的元素
|
||||
Collections.swap(arrayList, 2, 5);
|
||||
System.out.println("Collections.swap(arrayList, 2, 5):");
|
||||
System.out.println(arrayList);
|
||||
|
||||
// 定制排序的用法
|
||||
Collections.sort(arrayList, new Comparator<Integer>() {
|
||||
|
||||
@Override
|
||||
public int compare(Integer o1, Integer o2) {
|
||||
return o2.compareTo(o1);
|
||||
}
|
||||
});
|
||||
System.out.println("定制排序后:");
|
||||
System.out.println(arrayList);
|
||||
```
|
||||
|
||||
### 查找,替换操作
|
||||
|
||||
```java
|
||||
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
|
||||
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
|
||||
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
|
||||
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
|
||||
int frequency(Collection c, Object o)//统计元素出现次数
|
||||
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
|
||||
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
|
||||
```
|
||||
|
||||
**示例代码:**
|
||||
|
||||
```java
|
||||
ArrayList<Integer> arrayList = new ArrayList<Integer>();
|
||||
arrayList.add(-1);
|
||||
arrayList.add(3);
|
||||
arrayList.add(3);
|
||||
arrayList.add(-5);
|
||||
arrayList.add(7);
|
||||
arrayList.add(4);
|
||||
arrayList.add(-9);
|
||||
arrayList.add(-7);
|
||||
ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
|
||||
arrayList2.add(-3);
|
||||
arrayList2.add(-5);
|
||||
arrayList2.add(7);
|
||||
System.out.println("原始数组:");
|
||||
System.out.println(arrayList);
|
||||
|
||||
System.out.println("Collections.max(arrayList):");
|
||||
System.out.println(Collections.max(arrayList));
|
||||
|
||||
System.out.println("Collections.min(arrayList):");
|
||||
System.out.println(Collections.min(arrayList));
|
||||
|
||||
System.out.println("Collections.replaceAll(arrayList, 3, -3):");
|
||||
Collections.replaceAll(arrayList, 3, -3);
|
||||
System.out.println(arrayList);
|
||||
|
||||
System.out.println("Collections.frequency(arrayList, -3):");
|
||||
System.out.println(Collections.frequency(arrayList, -3));
|
||||
|
||||
System.out.println("Collections.indexOfSubList(arrayList, arrayList2):");
|
||||
System.out.println(Collections.indexOfSubList(arrayList, arrayList2));
|
||||
|
||||
System.out.println("Collections.binarySearch(arrayList, 7):");
|
||||
// 对List进行二分查找,返回索引,List必须是有序的
|
||||
Collections.sort(arrayList);
|
||||
System.out.println(Collections.binarySearch(arrayList, 7));
|
||||
```
|
||||
|
||||
### 同步控制
|
||||
|
||||
Collections提供了多个`synchronizedXxx()`方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。
|
||||
|
||||
我们知道 HashSet,TreeSet,ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的。Collections提供了多个静态方法可以把他们包装成线程同步的集合。
|
||||
|
||||
**最好不要用下面这些方法,效率非常低,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合。**
|
||||
|
||||
方法如下:
|
||||
|
||||
```java
|
||||
synchronizedCollection(Collection<T> c) //返回指定 collection 支持的同步(线程安全的)collection。
|
||||
synchronizedList(List<T> list)//返回指定列表支持的同步(线程安全的)List。
|
||||
synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安全的)Map。
|
||||
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的)set。
|
||||
```
|
||||
|
||||
### Collections还可以设置不可变集合,提供了如下三类方法:
|
||||
|
||||
```java
|
||||
emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是Set,还可以是Map。
|
||||
singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以是:List,Set,Map。
|
||||
unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是:List,Set,Map。
|
||||
上面三类方法的参数是原有的集合对象,返回值是该集合的”只读“版本。
|
||||
```
|
||||
|
||||
**示例代码:**
|
||||
|
||||
```java
|
||||
ArrayList<Integer> arrayList = new ArrayList<Integer>();
|
||||
arrayList.add(-1);
|
||||
arrayList.add(3);
|
||||
arrayList.add(3);
|
||||
arrayList.add(-5);
|
||||
arrayList.add(7);
|
||||
arrayList.add(4);
|
||||
arrayList.add(-9);
|
||||
arrayList.add(-7);
|
||||
HashSet<Integer> integers1 = new HashSet<>();
|
||||
integers1.add(1);
|
||||
integers1.add(3);
|
||||
integers1.add(2);
|
||||
Map scores = new HashMap();
|
||||
scores.put("语文" , 80);
|
||||
scores.put("Java" , 82);
|
||||
|
||||
//Collections.emptyXXX();创建一个空的、不可改变的XXX对象
|
||||
List<Object> list = Collections.emptyList();
|
||||
System.out.println(list);//[]
|
||||
Set<Object> objects = Collections.emptySet();
|
||||
System.out.println(objects);//[]
|
||||
Map<Object, Object> objectObjectMap = Collections.emptyMap();
|
||||
System.out.println(objectObjectMap);//{}
|
||||
|
||||
//Collections.singletonXXX();
|
||||
List<ArrayList<Integer>> arrayLists = Collections.singletonList(arrayList);
|
||||
System.out.println(arrayLists);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
|
||||
//创建一个只有一个元素,且不可改变的Set对象
|
||||
Set<ArrayList<Integer>> singleton = Collections.singleton(arrayList);
|
||||
System.out.println(singleton);//[[-1, 3, 3, -5, 7, 4, -9, -7]]
|
||||
Map<String, String> nihao = Collections.singletonMap("1", "nihao");
|
||||
System.out.println(nihao);//{1=nihao}
|
||||
|
||||
//unmodifiableXXX();创建普通XXX对象对应的不可变版本
|
||||
List<Integer> integers = Collections.unmodifiableList(arrayList);
|
||||
System.out.println(integers);//[-1, 3, 3, -5, 7, 4, -9, -7]
|
||||
Set<Integer> integers2 = Collections.unmodifiableSet(integers1);
|
||||
System.out.println(integers2);//[1, 2, 3]
|
||||
Map<Object, Object> objectObjectMap2 = Collections.unmodifiableMap(scores);
|
||||
System.out.println(objectObjectMap2);//{Java=82, 语文=80}
|
||||
|
||||
//添加出现异常:java.lang.UnsupportedOperationException
|
||||
// list.add(1);
|
||||
// arrayLists.add(arrayList);
|
||||
// integers.add(1);
|
||||
```
|
||||
|
||||
## Arrays类的常见操作
|
||||
1. 排序 : `sort()`
|
||||
2. 查找 : `binarySearch()`
|
||||
3. 比较: `equals()`
|
||||
4. 填充 : `fill()`
|
||||
5. 转列表: `asList()`
|
||||
6. 转字符串 : `toString()`
|
||||
7. 复制: `copyOf()`
|
||||
|
||||
|
||||
### 排序 : `sort()`
|
||||
|
||||
```java
|
||||
// *************排序 sort****************
|
||||
int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
|
||||
// sort(int[] a)方法按照数字顺序排列指定的数组。
|
||||
Arrays.sort(a);
|
||||
System.out.println("Arrays.sort(a):");
|
||||
for (int i : a) {
|
||||
System.out.print(i);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
|
||||
// sort(int[] a,int fromIndex,int toIndex)按升序排列数组的指定范围
|
||||
int b[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
|
||||
Arrays.sort(b, 2, 6);
|
||||
System.out.println("Arrays.sort(b, 2, 6):");
|
||||
for (int i : b) {
|
||||
System.out.print(i);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
|
||||
int c[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
|
||||
// parallelSort(int[] a) 按照数字顺序排列指定的数组(并行的)。同sort方法一样也有按范围的排序
|
||||
Arrays.parallelSort(c);
|
||||
System.out.println("Arrays.parallelSort(c):");
|
||||
for (int i : c) {
|
||||
System.out.print(i);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
|
||||
// parallelSort给字符数组排序,sort也可以
|
||||
char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
|
||||
Arrays.parallelSort(d);
|
||||
System.out.println("Arrays.parallelSort(d):");
|
||||
for (char d2 : d) {
|
||||
System.out.print(d2);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
|
||||
```
|
||||
|
||||
在做算法面试题的时候,我们还可能会经常遇到对字符串排序的情况,`Arrays.sort()` 对每个字符串的特定位置进行比较,然后按照升序排序。
|
||||
|
||||
```java
|
||||
String[] strs = { "abcdehg", "abcdefg", "abcdeag" };
|
||||
Arrays.sort(strs);
|
||||
System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg]
|
||||
```
|
||||
|
||||
### 查找 : `binarySearch()`
|
||||
|
||||
```java
|
||||
// *************查找 binarySearch()****************
|
||||
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
|
||||
// 排序后再进行二分查找,否则找不到
|
||||
Arrays.sort(e);
|
||||
System.out.println("Arrays.sort(e)" + Arrays.toString(e));
|
||||
System.out.println("Arrays.binarySearch(e, 'c'):");
|
||||
int s = Arrays.binarySearch(e, 'c');
|
||||
System.out.println("字符c在数组的位置:" + s);
|
||||
```
|
||||
|
||||
### 比较: `equals()`
|
||||
|
||||
```java
|
||||
// *************比较 equals****************
|
||||
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
|
||||
char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
|
||||
/*
|
||||
* 元素数量相同,并且相同位置的元素相同。 另外,如果两个数组引用都是null,则它们被认为是相等的 。
|
||||
*/
|
||||
// 输出true
|
||||
System.out.println("Arrays.equals(e, f):" + Arrays.equals(e, f));
|
||||
```
|
||||
|
||||
### 填充 : `fill()`
|
||||
|
||||
```java
|
||||
// *************填充fill(批量初始化)****************
|
||||
int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 };
|
||||
// 数组中所有元素重新分配值
|
||||
Arrays.fill(g, 3);
|
||||
System.out.println("Arrays.fill(g, 3):");
|
||||
// 输出结果:333333333
|
||||
for (int i : g) {
|
||||
System.out.print(i);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
|
||||
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
|
||||
// 数组中指定范围元素重新分配值
|
||||
Arrays.fill(h, 0, 2, 9);
|
||||
System.out.println("Arrays.fill(h, 0, 2, 9);:");
|
||||
// 输出结果:993333666
|
||||
for (int i : h) {
|
||||
System.out.print(i);
|
||||
}
|
||||
```
|
||||
|
||||
### 转列表 `asList()`
|
||||
|
||||
```java
|
||||
// *************转列表 asList()****************
|
||||
/*
|
||||
* 返回由指定数组支持的固定大小的列表。
|
||||
* (将返回的列表更改为“写入数组”。)该方法作为基于数组和基于集合的API之间的桥梁,与Collection.toArray()相结合 。
|
||||
* 返回的列表是可序列化的,并实现RandomAccess 。
|
||||
* 此方法还提供了一种方便的方式来创建一个初始化为包含几个元素的固定大小的列表如下:
|
||||
*/
|
||||
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
|
||||
System.out.println(stooges);
|
||||
```
|
||||
|
||||
### 转字符串 `toString()`
|
||||
|
||||
```java
|
||||
// *************转字符串 toString()****************
|
||||
/*
|
||||
* 返回指定数组的内容的字符串表示形式。
|
||||
*/
|
||||
char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
|
||||
System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B]
|
||||
```
|
||||
|
||||
### 复制 `copyOf()`
|
||||
|
||||
```java
|
||||
// *************复制 copy****************
|
||||
// copyOf 方法实现数组复制,h为数组,6为复制的长度
|
||||
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
|
||||
int i[] = Arrays.copyOf(h, 6);
|
||||
System.out.println("Arrays.copyOf(h, 6);:");
|
||||
// 输出结果:123333
|
||||
for (int j : i) {
|
||||
System.out.print(j);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
// copyOfRange将指定数组的指定范围复制到新数组中
|
||||
int j[] = Arrays.copyOfRange(h, 6, 11);
|
||||
System.out.println("Arrays.copyOfRange(h, 6, 11):");
|
||||
// 输出结果66600(h数组只有9个元素这里是从索引6到索引11复制所以不足的就为0)
|
||||
for (int j2 : j) {
|
||||
System.out.print(j2);
|
||||
}
|
||||
// 换行
|
||||
System.out.println();
|
||||
```
|
@ -13,9 +13,8 @@
|
||||
- [1.1.3. Oracle JDK 和 OpenJDK 的对比](#113-oracle-jdk-和-openjdk-的对比)
|
||||
- [1.1.4. Java 和 C++的区别?](#114-java-和-c的区别)
|
||||
- [1.1.5. 什么是 Java 程序的主类 应用程序和小程序的主类有何不同?](#115-什么是-java-程序的主类-应用程序和小程序的主类有何不同)
|
||||
- [1.1.6. Java 应用程序与小程序之间有哪些差别?](#116-java-应用程序与小程序之间有哪些差别)
|
||||
- [1.1.7. import java 和 javax 有什么区别?](#117-import-java-和-javax-有什么区别)
|
||||
- [1.1.8. 为什么说 Java 语言“编译与解释并存”?](#118-为什么说-java-语言编译与解释并存)
|
||||
- [1.1.6. import java 和 javax 有什么区别?](#116-import-java-和-javax-有什么区别)
|
||||
- [1.1.7. 为什么说 Java 语言“编译与解释并存”?](#117-为什么说-java-语言编译与解释并存)
|
||||
- [1.2. Java 语法](#12-java-语法)
|
||||
- [1.2.1. 字符型常量和字符串常量的区别?](#121-字符型常量和字符串常量的区别)
|
||||
- [1.2.2. 关于注释?](#122-关于注释)
|
||||
@ -34,8 +33,8 @@
|
||||
- [1.4.1. 什么是方法的返回值?返回值在类的方法里的作用是什么?](#141-什么是方法的返回值返回值在类的方法里的作用是什么)
|
||||
- [1.4.2. 为什么 Java 中只有值传递?](#142-为什么-java-中只有值传递)
|
||||
- [1.4.3. 重载和重写的区别](#143-重载和重写的区别)
|
||||
- [1.4.3.1. 重载](#1431-重载)
|
||||
- [1.4.3.2. 重写](#1432-重写)
|
||||
- [1.4.3.1. 重载](#1431-重载)
|
||||
- [1.4.3.2. 重写](#1432-重写)
|
||||
- [1.4.4. 深拷贝 vs 浅拷贝](#144-深拷贝-vs-浅拷贝)
|
||||
- [1.4.5. 方法的四种类型](#145-方法的四种类型)
|
||||
- [2. Java 面向对象](#2-java-面向对象)
|
||||
@ -70,8 +69,11 @@
|
||||
- [2.5.5. Java 序列化中如果有些字段不想进行序列化,怎么办?](#255-java-序列化中如果有些字段不想进行序列化怎么办)
|
||||
- [2.5.6. 获取用键盘输入常用的两种方法](#256-获取用键盘输入常用的两种方法)
|
||||
- [3. Java 核心技术](#3-java-核心技术)
|
||||
- [3.1. 集合](#31-集合)
|
||||
- [3.1.1. Collections 工具类和 Arrays 工具类常见方法总结](#311-collections-工具类和-arrays-工具类常见方法总结)
|
||||
- [3.1. 反射机制](#31-反射机制)
|
||||
- [3.1.1. 静态编译和动态编译](#311静态编译和动态编译)
|
||||
- [3.1.2. 反射机制优缺点](#312反射机制优缺点)
|
||||
- [3.1.3. 反射的应用场景](#313反射的应用场景)
|
||||
|
||||
- [3.2. 异常](#32-异常)
|
||||
- [3.2.1. Java 异常类层次结构图](#321-java-异常类层次结构图)
|
||||
- [3.2.2. Throwable 类常用方法](#322-throwable-类常用方法)
|
||||
@ -80,7 +82,7 @@
|
||||
- [3.3. 多线程](#33-多线程)
|
||||
- [3.3.1. 简述线程、程序、进程的基本概念。以及他们之间关系是什么?](#331-简述线程程序进程的基本概念以及他们之间关系是什么)
|
||||
- [3.3.2. 线程有哪些基本状态?](#332-线程有哪些基本状态)
|
||||
- [3.4. 文件与 I\O 流](#34-文件与-i\o-流)
|
||||
- [3.4. 文件与 I\O 流](#34-文件与-io-流)
|
||||
- [3.4.1. Java 中 IO 流分为几种?](#341-java-中-io-流分为几种)
|
||||
- [3.4.1.1. 既然有了字节流,为什么还要有字符流?](#3411-既然有了字节流为什么还要有字符流)
|
||||
- [3.4.1.2. BIO,NIO,AIO 有什么区别?](#3412-bionioaio-有什么区别)
|
||||
@ -169,17 +171,13 @@ JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有
|
||||
|
||||
一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指包含 `main()` 方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。
|
||||
|
||||
#### 1.1.6. Java 应用程序与小程序之间有哪些差别?
|
||||
|
||||
简单说应用程序是从主线程启动(也就是 `main()` 方法)。applet 小程序没有 `main()` 方法,主要是嵌在浏览器页面上运行(调用`init()`或者`run()`来启动),嵌入浏览器这点跟 flash 的小游戏类似。
|
||||
|
||||
#### 1.1.7. import java 和 javax 有什么区别?
|
||||
#### 1.1.6. import java 和 javax 有什么区别?
|
||||
|
||||
刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。
|
||||
|
||||
所以,实际上 java 和 javax 没有区别。这都是一个名字。
|
||||
|
||||
#### 1.1.8. 为什么说 Java 语言“编译与解释并存”?
|
||||
#### 1.1.7. 为什么说 Java 语言“编译与解释并存”?
|
||||
|
||||
高级编程语言按照程序的执行方式分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。比如,你想阅读一本英文名著,你可以找一个英文翻译人员帮助你阅读,
|
||||
有两种选择方式,你可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读,也可以让翻译人员翻译一段,你在旁边阅读一段,慢慢把书读完。
|
||||
@ -771,7 +769,7 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象
|
||||
>
|
||||
> 重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法
|
||||
|
||||
###### 1.4.3.1. 重载
|
||||
**重载:**
|
||||
|
||||
发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
|
||||
|
||||
@ -779,9 +777,9 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象
|
||||
|
||||

|
||||
|
||||
**综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。**
|
||||
综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。
|
||||
|
||||
###### 1.4.3.2. 重写
|
||||
**重写:**
|
||||
|
||||
重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
|
||||
|
||||
@ -789,9 +787,9 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象
|
||||
2. 如果父类方法访问修饰符为 `private/final/static` 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。
|
||||
3. 构造方法无法被重写
|
||||
|
||||
**综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变**
|
||||
综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变
|
||||
|
||||
**暖心的 Guide 哥最后再来个图表总结一下!**
|
||||
暖心的 Guide 哥最后再来个图表总结一下!
|
||||
|
||||
| 区别点 | 重载方法 | 重写方法 |
|
||||
| :--------- | :------- | :----------------------------------------------------------- |
|
||||
@ -1178,11 +1176,32 @@ String s = input.readLine();
|
||||
|
||||
## 3. Java 核心技术
|
||||
|
||||
### 3.1. 集合
|
||||
### 3.1. 反射机制
|
||||
|
||||
#### 3.1.1. Collections 工具类和 Arrays 工具类常见方法总结
|
||||
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
|
||||
|
||||
详见笔主的这篇文章: https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/basic/Arrays,CollectionsCommonMethods.md
|
||||
#### 3.1.1.静态编译和动态编译
|
||||
|
||||
- **静态编译:** 在编译时确定类型,绑定对象
|
||||
- **动态编译:** 运行时确定类型,绑定对象
|
||||
|
||||
#### 3.1.2.反射机制优缺点
|
||||
|
||||
- **优点:** 运行期类型的判断,动态加载类,提高代码灵活度。
|
||||
- **缺点:** 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。
|
||||
|
||||
#### 3.1.3.反射的应用场景
|
||||
|
||||
**反射是框架设计的灵魂。**
|
||||
|
||||
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
|
||||
|
||||
举例:
|
||||
|
||||
1. 我们在使用 JDBC 连接数据库时使用 `Class.forName()`通过反射加载数据库的驱动程序;
|
||||
2. Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
|
||||
3. 动态配置实例的属性;
|
||||
4. ......
|
||||
|
||||
### 3.2. 异常
|
||||
|
||||
@ -1334,7 +1353,7 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种
|
||||
|
||||

|
||||
|
||||
当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。
|
||||
当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。
|
||||
|
||||
### 3.4. 文件与 I\O 流
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
|
||||
|
||||
### 获取 Class 对象的两种方式
|
||||
### 获取 Class 对象的四种方式
|
||||
|
||||
如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了三种方式获取 Class 对象:
|
||||
如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象:
|
||||
|
||||
1.知道具体类的情况下可以使用:
|
||||
|
||||
@ -12,18 +12,29 @@ JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道
|
||||
Class alunbarClass = TargetObject.class;
|
||||
```
|
||||
|
||||
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象
|
||||
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取Class对象不会进行初始化
|
||||
|
||||
2.通过 `Class.forName()`传入类的路径获取:
|
||||
|
||||
```java
|
||||
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
|
||||
```
|
||||
Class.forName(className)方法,内部实际调用的是一个native方法 forName0(className, true, ClassLoader.getClassLoader(caller), caller);
|
||||
|
||||
第2个boolean参数表示类是否需要初始化,Class.forName(className)默认是需要初始化。
|
||||
|
||||
一旦初始化,就会触发目标对象的 static块代码执行,static参数也会被再次初始化。
|
||||
|
||||
3.通过对象实例`instance.getClass()`获取:
|
||||
```
|
||||
Employee e;
|
||||
```java
|
||||
Employee e = new Employee();
|
||||
Class alunbarClass2 = e.getClass();
|
||||
```
|
||||
4.通过类加载器`xxxClassLoader.loadClass()`传入类路径获取
|
||||
```java
|
||||
class clazz = ClassLoader.LoadClass("cn.javaguide.TargetObject");
|
||||
```
|
||||
通过类加载器获取Class对象不会进行初始化,意味着不进行包括初始化等一些列步骤,静态块和静态对象不会得到执行
|
||||
|
||||
### 代码实例
|
||||
|
@ -69,7 +69,7 @@ if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED));
|
||||
if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED);
|
||||
```
|
||||
|
||||
对于编译时安全性,我们看另一个示例,两个不同枚举类型进行比较,使用equal方法比较结果确定为true,因为getStatus方法的枚举值与另一个类型枚举值一致,但逻辑上应该为false。这个问题可以使用==操作符避免。因为编译器会表示类型不兼容错误:
|
||||
对于编译时安全性,我们看另一个示例,两个不同枚举类型进行比较,使用equal方法比较结果确定为true,因为`getStatus`方法的枚举值与另一个类型枚举值一致,但逻辑上应该为false。这个问题可以使用==操作符避免。因为编译器会表示类型不兼容错误:
|
||||
|
||||
```java
|
||||
if(testPz.getStatus().equals(TestColor.GREEN));
|
@ -1,361 +0,0 @@
|
||||
|
||||
## 一 先从 ArrayList 的构造函数说起
|
||||
|
||||
**ArrayList有三种方式来初始化,构造方法源码如下:**
|
||||
|
||||
```java
|
||||
/**
|
||||
* 默认初始容量大小
|
||||
*/
|
||||
private static final int DEFAULT_CAPACITY = 10;
|
||||
|
||||
|
||||
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
|
||||
|
||||
/**
|
||||
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
|
||||
*/
|
||||
public ArrayList() {
|
||||
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 带初始容量参数的构造函数。(用户自己指定容量)
|
||||
*/
|
||||
public ArrayList(int initialCapacity) {
|
||||
if (initialCapacity > 0) {//初始容量大于0
|
||||
//创建initialCapacity大小的数组
|
||||
this.elementData = new Object[initialCapacity];
|
||||
} else if (initialCapacity == 0) {//初始容量等于0
|
||||
//创建空数组
|
||||
this.elementData = EMPTY_ELEMENTDATA;
|
||||
} else {//初始容量小于0,抛出异常
|
||||
throw new IllegalArgumentException("Illegal Capacity: "+
|
||||
initialCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
|
||||
*如果指定的集合为null,throws NullPointerException。
|
||||
*/
|
||||
public ArrayList(Collection<? extends E> c) {
|
||||
elementData = c.toArray();
|
||||
if ((size = elementData.length) != 0) {
|
||||
// c.toArray might (incorrectly) not return Object[] (see 6260652)
|
||||
if (elementData.getClass() != Object[].class)
|
||||
elementData = Arrays.copyOf(elementData, size, Object[].class);
|
||||
} else {
|
||||
// replace with empty array.
|
||||
this.elementData = EMPTY_ELEMENTDATA;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
|
||||
|
||||
## 二 一步一步分析 ArrayList 扩容机制
|
||||
|
||||
这里以无参构造函数创建的 ArrayList 为例分析
|
||||
|
||||
### 1. 先来看 `add` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 将指定的元素追加到此列表的末尾。
|
||||
*/
|
||||
public boolean add(E e) {
|
||||
//添加元素之前,先调用ensureCapacityInternal方法
|
||||
ensureCapacityInternal(size + 1); // Increments modCount!!
|
||||
//这里看到ArrayList添加元素的实质就相当于为数组赋值
|
||||
elementData[size++] = e;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
> **注意** :JDK11 移除了 `ensureCapacityInternal()` 和 `ensureExplicitCapacity()` 方法
|
||||
|
||||
### 2. 再来看看 `ensureCapacityInternal()` 方法
|
||||
|
||||
可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)`
|
||||
|
||||
```java
|
||||
//得到最小扩容量
|
||||
private void ensureCapacityInternal(int minCapacity) {
|
||||
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
|
||||
// 获取默认的容量和传入参数的较大值
|
||||
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
|
||||
}
|
||||
|
||||
ensureExplicitCapacity(minCapacity);
|
||||
}
|
||||
```
|
||||
**当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。**
|
||||
|
||||
### 3. `ensureExplicitCapacity()` 方法
|
||||
|
||||
如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码!
|
||||
|
||||
```java
|
||||
//判断是否需要扩容
|
||||
private void ensureExplicitCapacity(int minCapacity) {
|
||||
modCount++;
|
||||
|
||||
// overflow-conscious code
|
||||
if (minCapacity - elementData.length > 0)
|
||||
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
|
||||
grow(minCapacity);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们来仔细分析一下:
|
||||
|
||||
- 当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为10。此时,`minCapacity - elementData.length > 0 `成立,所以会进入 `grow(minCapacity)` 方法。
|
||||
- 当add第2个元素时,minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0 ` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。
|
||||
- 添加第3、4···到第10个元素时,依然不会执行grow方法,数组容量都为10。
|
||||
|
||||
直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进入grow方法进行扩容。
|
||||
|
||||
### 4. `grow()` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 要分配的最大数组大小
|
||||
*/
|
||||
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
/**
|
||||
* ArrayList扩容的核心方法。
|
||||
*/
|
||||
private void grow(int minCapacity) {
|
||||
// oldCapacity为旧容量,newCapacity为新容量
|
||||
int oldCapacity = elementData.length;
|
||||
//将oldCapacity 右移一位,其效果相当于oldCapacity /2,
|
||||
//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
|
||||
int newCapacity = oldCapacity + (oldCapacity >> 1);
|
||||
//然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
|
||||
if (newCapacity - minCapacity < 0)
|
||||
newCapacity = minCapacity;
|
||||
// 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
|
||||
//如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
|
||||
if (newCapacity - MAX_ARRAY_SIZE > 0)
|
||||
newCapacity = hugeCapacity(minCapacity);
|
||||
// minCapacity is usually close to size, so this is a win:
|
||||
elementData = Arrays.copyOf(elementData, newCapacity);
|
||||
}
|
||||
```
|
||||
|
||||
**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity为偶数就是1.5倍,否则是1.5倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数.
|
||||
|
||||
> ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
|
||||
|
||||
**我们再来通过例子探究一下`grow()` 方法 :**
|
||||
|
||||
- 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 `hugeCapacity` 方法。数组容量为10,add方法中 return true,size增为1。
|
||||
- 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11。
|
||||
- 以此类推······
|
||||
|
||||
**这里补充一点比较重要,但是容易被忽视掉的知识点:**
|
||||
|
||||
- java 中的 `length `属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
|
||||
- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法.
|
||||
- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
|
||||
|
||||
### 5. `hugeCapacity()` 方法。
|
||||
|
||||
从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
|
||||
|
||||
|
||||
```java
|
||||
private static int hugeCapacity(int minCapacity) {
|
||||
if (minCapacity < 0) // overflow
|
||||
throw new OutOfMemoryError();
|
||||
//对minCapacity和MAX_ARRAY_SIZE进行比较
|
||||
//若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
|
||||
//若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
|
||||
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||||
return (minCapacity > MAX_ARRAY_SIZE) ?
|
||||
Integer.MAX_VALUE :
|
||||
MAX_ARRAY_SIZE;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 三 `System.arraycopy()` 和 `Arrays.copyOf()`方法
|
||||
|
||||
|
||||
阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)`、`toArray()` 等方法中都用到了该方法!
|
||||
|
||||
|
||||
### 3.1 `System.arraycopy()` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 在此列表中的指定位置插入指定的元素。
|
||||
*先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
|
||||
*再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
|
||||
*/
|
||||
public void add(int index, E element) {
|
||||
rangeCheckForAdd(index);
|
||||
|
||||
ensureCapacityInternal(size + 1); // Increments modCount!!
|
||||
//arraycopy()方法实现数组自己复制自己
|
||||
//elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量;
|
||||
System.arraycopy(elementData, index, elementData, index + 1, size - index);
|
||||
elementData[index] = element;
|
||||
size++;
|
||||
}
|
||||
```
|
||||
|
||||
我们写一个简单的方法测试以下:
|
||||
|
||||
```java
|
||||
public class ArraycopyTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
int[] a = new int[10];
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[2] = 2;
|
||||
a[3] = 3;
|
||||
System.arraycopy(a, 2, a, 3, 3);
|
||||
a[2]=99;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
System.out.print(a[i] + " ");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```
|
||||
0 1 99 2 3 0 0 0 0 0
|
||||
```
|
||||
|
||||
### 3.2 `Arrays.copyOf()`方法
|
||||
|
||||
```java
|
||||
/**
|
||||
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
|
||||
*/
|
||||
public Object[] toArray() {
|
||||
//elementData:要复制的数组;size:要复制的长度
|
||||
return Arrays.copyOf(elementData, size);
|
||||
}
|
||||
```
|
||||
|
||||
个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下:
|
||||
|
||||
```java
|
||||
public class ArrayscopyOfTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int[] a = new int[3];
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[2] = 2;
|
||||
int[] b = Arrays.copyOf(a, 10);
|
||||
System.out.println("b.length"+b.length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```
|
||||
10
|
||||
```
|
||||
|
||||
### 3.3 两者联系和区别
|
||||
|
||||
**联系:**
|
||||
|
||||
看两者源代码可以发现 copyOf() 内部实际调用了 `System.arraycopy()` 方法
|
||||
|
||||
**区别:**
|
||||
|
||||
`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
|
||||
|
||||
## 四 `ensureCapacity`方法
|
||||
|
||||
ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
|
||||
|
||||
```java
|
||||
/**
|
||||
如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
|
||||
*
|
||||
* @param minCapacity 所需的最小容量
|
||||
*/
|
||||
public void ensureCapacity(int minCapacity) {
|
||||
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
|
||||
// any size if not default element table
|
||||
? 0
|
||||
// larger than default for default empty table. It's already
|
||||
// supposed to be at default size.
|
||||
: DEFAULT_CAPACITY;
|
||||
|
||||
if (minCapacity > minExpand) {
|
||||
ensureExplicitCapacity(minCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数**
|
||||
|
||||
我们通过下面的代码实际测试以下这个方法的效果:
|
||||
|
||||
```java
|
||||
public class EnsureCapacityTest {
|
||||
public static void main(String[] args) {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
final int N = 10000000;
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < N; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||
```
|
||||
使用ensureCapacity方法前:2158
|
||||
```
|
||||
|
||||
```java
|
||||
public class EnsureCapacityTest {
|
||||
public static void main(String[] args) {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
final int N = 10000000;
|
||||
list = new ArrayList<Object>();
|
||||
long startTime1 = System.currentTimeMillis();
|
||||
list.ensureCapacity(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
long endTime1 = System.currentTimeMillis();
|
||||
System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||
```
|
||||
|
||||
使用ensureCapacity方法前:1773
|
||||
```
|
||||
|
||||
通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。
|
@ -1,34 +1,35 @@
|
||||
<!-- MarkdownTOC -->
|
||||
## 1. ArrayList 简介
|
||||
|
||||
- [ArrayList简介](#arraylist简介)
|
||||
- [ArrayList核心源码](#arraylist核心源码)
|
||||
- [ArrayList源码分析](#arraylist源码分析)
|
||||
- [System.arraycopy\(\)和Arrays.copyOf\(\)方法](#systemarraycopy和arrayscopyof方法)
|
||||
- [两者联系与区别](#两者联系与区别)
|
||||
- [ArrayList核心扩容技术](#arraylist核心扩容技术)
|
||||
- [内部类](#内部类)
|
||||
- [ArrayList经典Demo](#arraylist经典demo)
|
||||
`ArrayList` 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用`ensureCapacity`操作来增加 `ArrayList` 实例的容量。这可以减少递增式再分配的数量。
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
`ArrayList`继承于 **`AbstractList`**,实现了 **`List`**, **`RandomAccess`**, **`Cloneable`**, **`java.io.Serializable`** 这些接口。
|
||||
|
||||
```java
|
||||
|
||||
### ArrayList简介
|
||||
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用`ensureCapacity`操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
|
||||
|
||||
它继承于 **AbstractList**,实现了 **List**, **RandomAccess**, **Cloneable**, **java.io.Serializable** 这些接口。
|
||||
|
||||
在我们学数据结构的时候就知道了线性表的顺序存储,插入删除元素的时间复杂度为**O(n)**,求表长以及增加元素,取第 i 元素的时间复杂度为**O(1)**
|
||||
public class ArrayList<E> extends AbstractList<E>
|
||||
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
|
||||
|
||||
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
|
||||
}
|
||||
```
|
||||
|
||||
ArrayList 实现了**RandomAccess 接口**, RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持**快速随机访问**的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
|
||||
- `RandomAccess` 是一个标志接口,表明实现这个这个接口的 List 集合是支持**快速随机访问**的。在 `ArrayList` 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
|
||||
- `ArrayList` 实现了 **`Cloneable` 接口** ,即覆盖了函数`clone()`,能被克隆。
|
||||
- `ArrayList` 实现了 java.io.Serializable `接口,这意味着`ArrayList`支持序列化,能通过序列化去传输。
|
||||
|
||||
ArrayList 实现了**Cloneable 接口**,即覆盖了函数 clone(),**能被克隆**。
|
||||
### 1.1. Arraylist 和 Vector 的区别?
|
||||
|
||||
ArrayList 实现**java.io.Serializable 接口**,这意味着ArrayList**支持序列化**,**能通过序列化去传输**。
|
||||
1. `ArrayList` 是 `List` 的主要实现类,底层使用 `Object[ ]`存储,适用于频繁的查找工作,线程不安全 ;
|
||||
2. `Vector` 是 `List` 的古老实现类,底层使用 `Object[ ]`存储,线程安全的。
|
||||
|
||||
和 Vector 不同,**ArrayList 中的操作不是线程安全的**!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。
|
||||
### ArrayList核心源码
|
||||
### 1.2. Arraylist 与 LinkedList 区别?
|
||||
|
||||
1. **是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全;
|
||||
2. **底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
|
||||
3. **插入和删除是否受元素位置的影响:** ① **`ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e)`方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element)`)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **`LinkedList` 采用链表存储,所以对于`add(E e)`方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置`i`插入和删除元素的话(`(add(int index, E element)`) 时间复杂度近似为`o(n))`因为需要先移动到指定位置再插入。**
|
||||
4. **是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index)`方法)。
|
||||
5. **内存空间占用:** `ArrayList` 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 `LinkedList` 的空间花费则体现在它的每一个元素都需要消耗比 `ArrayList` 更多的空间(因为要存放直接后继和直接前驱以及数据)。
|
||||
|
||||
## 2. ArrayList 核心源码解读
|
||||
|
||||
```java
|
||||
package java.util;
|
||||
@ -100,7 +101,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
elementData = c.toArray();
|
||||
//如果elementData数组的长度不为0
|
||||
if ((size = elementData.length) != 0) {
|
||||
// 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)
|
||||
// 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)
|
||||
if (elementData.getClass() != Object[].class)
|
||||
//将原来不是Object类型的elementData数组的内容,赋值给新的Object类型的elementData数组
|
||||
elementData = Arrays.copyOf(elementData, size, Object[].class);
|
||||
@ -111,7 +112,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。
|
||||
* 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。
|
||||
*/
|
||||
public void trimToSize() {
|
||||
modCount++;
|
||||
@ -195,7 +196,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
*返回此列表中的元素数。
|
||||
*返回此列表中的元素数。
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
@ -213,12 +214,12 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
* 如果此列表包含指定的元素,则返回true 。
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
//indexOf()方法:返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1
|
||||
//indexOf()方法:返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1
|
||||
*返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1
|
||||
*/
|
||||
public int indexOf(Object o) {
|
||||
if (o == null) {
|
||||
@ -251,7 +252,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此ArrayList实例的浅拷贝。 (元素本身不被复制。)
|
||||
* 返回此ArrayList实例的浅拷贝。 (元素本身不被复制。)
|
||||
*/
|
||||
public Object clone() {
|
||||
try {
|
||||
@ -267,7 +268,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
*以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
|
||||
*以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
|
||||
*返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。
|
||||
*因此,调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。
|
||||
*/
|
||||
@ -276,11 +277,11 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素);
|
||||
*返回的数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组,则返回其中。
|
||||
*否则,将为指定数组的运行时类型和此列表的大小分配一个新数组。
|
||||
* 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素);
|
||||
*返回的数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组,则返回其中。
|
||||
*否则,将为指定数组的运行时类型和此列表的大小分配一个新数组。
|
||||
*如果列表适用于指定的数组,其余空间(即数组的列表数量多于此元素),则紧跟在集合结束后的数组中的元素设置为null 。
|
||||
*(这仅在调用者知道列表不包含任何空元素的情况下才能确定列表的长度。)
|
||||
*(这仅在调用者知道列表不包含任何空元素的情况下才能确定列表的长度。)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] a) {
|
||||
@ -311,7 +312,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 用指定的元素替换此列表中指定位置的元素。
|
||||
* 用指定的元素替换此列表中指定位置的元素。
|
||||
*/
|
||||
public E set(int index, E element) {
|
||||
//对index进行界限检查
|
||||
@ -324,7 +325,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定的元素追加到此列表的末尾。
|
||||
* 将指定的元素追加到此列表的末尾。
|
||||
*/
|
||||
public boolean add(E e) {
|
||||
ensureCapacityInternal(size + 1); // Increments modCount!!
|
||||
@ -334,7 +335,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 在此列表中的指定位置插入指定的元素。
|
||||
* 在此列表中的指定位置插入指定的元素。
|
||||
*先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
|
||||
*再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
|
||||
*/
|
||||
@ -350,7 +351,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除该列表中指定位置的元素。 将任何后续元素移动到左侧(从其索引中减去一个元素)。
|
||||
* 删除该列表中指定位置的元素。 将任何后续元素移动到左侧(从其索引中减去一个元素)。
|
||||
*/
|
||||
public E remove(int index) {
|
||||
rangeCheck(index);
|
||||
@ -363,7 +364,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
System.arraycopy(elementData, index+1, elementData, index,
|
||||
numMoved);
|
||||
elementData[--size] = null; // clear to let GC do its work
|
||||
//从列表中删除的元素
|
||||
//从列表中删除的元素
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@ -402,7 +403,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中删除所有元素。
|
||||
* 从列表中删除所有元素。
|
||||
*/
|
||||
public void clear() {
|
||||
modCount++;
|
||||
@ -488,7 +489,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* 从此列表中删除指定集合中包含的所有元素。
|
||||
* 从此列表中删除指定集合中包含的所有元素。
|
||||
*/
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
@ -498,7 +499,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
|
||||
/**
|
||||
* 仅保留此列表中包含在指定集合中的元素。
|
||||
*换句话说,从此列表中删除其中不包含在指定集合中的所有元素。
|
||||
*换句话说,从此列表中删除其中不包含在指定集合中的所有元素。
|
||||
*/
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
Objects.requireNonNull(c);
|
||||
@ -508,8 +509,8 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
|
||||
/**
|
||||
* 从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。
|
||||
*指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。
|
||||
*返回的列表迭代器是fail-fast 。
|
||||
*指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。
|
||||
*返回的列表迭代器是fail-fast 。
|
||||
*/
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
if (index < 0 || index > size)
|
||||
@ -518,7 +519,7 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
*返回列表中的列表迭代器(按适当的顺序)。
|
||||
*返回列表中的列表迭代器(按适当的顺序)。
|
||||
*返回的列表迭代器是fail-fast 。
|
||||
*/
|
||||
public ListIterator<E> listIterator() {
|
||||
@ -526,21 +527,211 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
}
|
||||
|
||||
/**
|
||||
*以正确的顺序返回该列表中的元素的迭代器。
|
||||
*返回的迭代器是fail-fast 。
|
||||
*以正确的顺序返回该列表中的元素的迭代器。
|
||||
*返回的迭代器是fail-fast 。
|
||||
*/
|
||||
public Iterator<E> iterator() {
|
||||
return new Itr();
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
### <font face="楷体" id="1" id="5">ArrayList源码分析</font>
|
||||
#### System.arraycopy()和Arrays.copyOf()方法
|
||||
通过上面源码我们发现这两个实现数组复制的方法被广泛使用而且很多地方都特别巧妙。比如下面<font color="red">add(int index, E element)</font>方法就很巧妙的用到了<font color="red">arraycopy()方法</font>让数组自己复制自己实现让index开始之后的所有成员后移一个位置:
|
||||
```java
|
||||
|
||||
## 3. ArrayList 扩容机制分析
|
||||
|
||||
### 3.1. 先从 ArrayList 的构造函数说起
|
||||
|
||||
**ArrayList 有三种方式来初始化,构造方法源码如下:**
|
||||
|
||||
```java
|
||||
/**
|
||||
* 默认初始容量大小
|
||||
*/
|
||||
private static final int DEFAULT_CAPACITY = 10;
|
||||
|
||||
|
||||
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
|
||||
|
||||
/**
|
||||
* 在此列表中的指定位置插入指定的元素。
|
||||
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
|
||||
*/
|
||||
public ArrayList() {
|
||||
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 带初始容量参数的构造函数。(用户自己指定容量)
|
||||
*/
|
||||
public ArrayList(int initialCapacity) {
|
||||
if (initialCapacity > 0) {//初始容量大于0
|
||||
//创建initialCapacity大小的数组
|
||||
this.elementData = new Object[initialCapacity];
|
||||
} else if (initialCapacity == 0) {//初始容量等于0
|
||||
//创建空数组
|
||||
this.elementData = EMPTY_ELEMENTDATA;
|
||||
} else {//初始容量小于0,抛出异常
|
||||
throw new IllegalArgumentException("Illegal Capacity: "+
|
||||
initialCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
|
||||
*如果指定的集合为null,throws NullPointerException。
|
||||
*/
|
||||
public ArrayList(Collection<? extends E> c) {
|
||||
elementData = c.toArray();
|
||||
if ((size = elementData.length) != 0) {
|
||||
// c.toArray might (incorrectly) not return Object[] (see 6260652)
|
||||
if (elementData.getClass() != Object[].class)
|
||||
elementData = Arrays.copyOf(elementData, size, Object[].class);
|
||||
} else {
|
||||
// replace with empty array.
|
||||
this.elementData = EMPTY_ELEMENTDATA;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
|
||||
|
||||
### 3.2. 一步一步分析 ArrayList 扩容机制
|
||||
|
||||
这里以无参构造函数创建的 ArrayList 为例分析
|
||||
|
||||
#### 3.2.1. 先来看 `add` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 将指定的元素追加到此列表的末尾。
|
||||
*/
|
||||
public boolean add(E e) {
|
||||
//添加元素之前,先调用ensureCapacityInternal方法
|
||||
ensureCapacityInternal(size + 1); // Increments modCount!!
|
||||
//这里看到ArrayList添加元素的实质就相当于为数组赋值
|
||||
elementData[size++] = e;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
> **注意** :JDK11 移除了 `ensureCapacityInternal()` 和 `ensureExplicitCapacity()` 方法
|
||||
|
||||
#### 3.2.2. 再来看看 `ensureCapacityInternal()` 方法
|
||||
|
||||
可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)`
|
||||
|
||||
```java
|
||||
//得到最小扩容量
|
||||
private void ensureCapacityInternal(int minCapacity) {
|
||||
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
|
||||
// 获取默认的容量和传入参数的较大值
|
||||
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
|
||||
}
|
||||
|
||||
ensureExplicitCapacity(minCapacity);
|
||||
}
|
||||
```
|
||||
|
||||
**当 要 add 进第 1 个元素时,minCapacity 为 1,在 Math.max()方法比较后,minCapacity 为 10。**
|
||||
|
||||
#### 3.2.3. `ensureExplicitCapacity()` 方法
|
||||
|
||||
如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码!
|
||||
|
||||
```java
|
||||
//判断是否需要扩容
|
||||
private void ensureExplicitCapacity(int minCapacity) {
|
||||
modCount++;
|
||||
|
||||
// overflow-conscious code
|
||||
if (minCapacity - elementData.length > 0)
|
||||
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
|
||||
grow(minCapacity);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们来仔细分析一下:
|
||||
|
||||
- 当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为 10。此时,`minCapacity - elementData.length > 0`成立,所以会进入 `grow(minCapacity)` 方法。
|
||||
- 当 add 第 2 个元素时,minCapacity 为 2,此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。
|
||||
- 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。
|
||||
|
||||
直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大。进入 grow 方法进行扩容。
|
||||
|
||||
#### 3.2.4. `grow()` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 要分配的最大数组大小
|
||||
*/
|
||||
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
/**
|
||||
* ArrayList扩容的核心方法。
|
||||
*/
|
||||
private void grow(int minCapacity) {
|
||||
// oldCapacity为旧容量,newCapacity为新容量
|
||||
int oldCapacity = elementData.length;
|
||||
//将oldCapacity 右移一位,其效果相当于oldCapacity /2,
|
||||
//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
|
||||
int newCapacity = oldCapacity + (oldCapacity >> 1);
|
||||
//然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
|
||||
if (newCapacity - minCapacity < 0)
|
||||
newCapacity = minCapacity;
|
||||
// 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
|
||||
//如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
|
||||
if (newCapacity - MAX_ARRAY_SIZE > 0)
|
||||
newCapacity = hugeCapacity(minCapacity);
|
||||
// minCapacity is usually close to size, so this is a win:
|
||||
elementData = Arrays.copyOf(elementData, newCapacity);
|
||||
}
|
||||
```
|
||||
|
||||
**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数.
|
||||
|
||||
> ">>"(移位运算符):>>1 右移一位相当于除 2,右移 n 位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了 1 位所以相当于 oldCapacity /2。对于大数据的 2 进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
|
||||
|
||||
**我们再来通过例子探究一下`grow()` 方法 :**
|
||||
|
||||
- 当 add 第 1 个元素时,oldCapacity 为 0,经比较后第一个 if 判断成立,newCapacity = minCapacity(为 10)。但是第二个 if 判断不会成立,即 newCapacity 不比 MAX_ARRAY_SIZE 大,则不会进入 `hugeCapacity` 方法。数组容量为 10,add 方法中 return true,size 增为 1。
|
||||
- 当 add 第 11 个元素进入 grow 方法时,newCapacity 为 15,比 minCapacity(为 11)大,第一个 if 判断不成立。新容量没有大于数组最大 size,不会进入 hugeCapacity 方法。数组容量扩为 15,add 方法中 return true,size 增为 11。
|
||||
- 以此类推······
|
||||
|
||||
**这里补充一点比较重要,但是容易被忽视掉的知识点:**
|
||||
|
||||
- java 中的 `length`属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
|
||||
- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法.
|
||||
- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
|
||||
|
||||
#### 3.2.5. `hugeCapacity()` 方法。
|
||||
|
||||
从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果 minCapacity 大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
|
||||
|
||||
```java
|
||||
private static int hugeCapacity(int minCapacity) {
|
||||
if (minCapacity < 0) // overflow
|
||||
throw new OutOfMemoryError();
|
||||
//对minCapacity和MAX_ARRAY_SIZE进行比较
|
||||
//若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
|
||||
//若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
|
||||
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||||
return (minCapacity > MAX_ARRAY_SIZE) ?
|
||||
Integer.MAX_VALUE :
|
||||
MAX_ARRAY_SIZE;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. `System.arraycopy()` 和 `Arrays.copyOf()`方法
|
||||
|
||||
阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)`、`toArray()` 等方法中都用到了该方法!
|
||||
|
||||
#### 3.3.1. `System.arraycopy()` 方法
|
||||
|
||||
```java
|
||||
/**
|
||||
* 在此列表中的指定位置插入指定的元素。
|
||||
*先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
|
||||
*再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
|
||||
*/
|
||||
@ -555,32 +746,87 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
size++;
|
||||
}
|
||||
```
|
||||
又如toArray()方法中用到了copyOf()方法
|
||||
```java
|
||||
|
||||
/**
|
||||
*以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
|
||||
*返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。
|
||||
*因此,调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。
|
||||
我们写一个简单的方法测试以下:
|
||||
|
||||
```java
|
||||
public class ArraycopyTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
int[] a = new int[10];
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[2] = 2;
|
||||
a[3] = 3;
|
||||
System.arraycopy(a, 2, a, 3, 3);
|
||||
a[2]=99;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
System.out.print(a[i] + " ");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```
|
||||
0 1 99 2 3 0 0 0 0 0
|
||||
```
|
||||
|
||||
#### 3.3.2. `Arrays.copyOf()`方法
|
||||
|
||||
```java
|
||||
/**
|
||||
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
|
||||
*/
|
||||
public Object[] toArray() {
|
||||
//elementData:要复制的数组;size:要复制的长度
|
||||
return Arrays.copyOf(elementData, size);
|
||||
}
|
||||
```
|
||||
##### 两者联系与区别
|
||||
**联系:**
|
||||
看两者源代码可以发现`copyOf()`内部调用了`System.arraycopy()`方法
|
||||
**区别:**
|
||||
1. arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长度以及放入新数组中的位置
|
||||
2. copyOf()是系统自动在内部新建一个数组,并返回该数组。
|
||||
#### ArrayList 核心扩容技术
|
||||
|
||||
个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下:
|
||||
|
||||
```java
|
||||
public class ArrayscopyOfTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int[] a = new int[3];
|
||||
a[0] = 0;
|
||||
a[1] = 1;
|
||||
a[2] = 2;
|
||||
int[] b = Arrays.copyOf(a, 10);
|
||||
System.out.println("b.length"+b.length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```
|
||||
10
|
||||
```
|
||||
|
||||
#### 3.3.3. 两者联系和区别
|
||||
|
||||
**联系:**
|
||||
|
||||
看两者源代码可以发现 `copyOf()`内部实际调用了 `System.arraycopy()` 方法
|
||||
|
||||
**区别:**
|
||||
|
||||
`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
|
||||
|
||||
### 3.4. `ensureCapacity`方法
|
||||
|
||||
ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
|
||||
|
||||
```java
|
||||
//下面是ArrayList的扩容机制
|
||||
//ArrayList的扩容机制提高了性能,如果每次只扩充一个,
|
||||
//那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList的扩容机制避免了这种情况。
|
||||
/**
|
||||
* 如有必要,增加此ArrayList实例的容量,以确保它至少能容纳元素的数量
|
||||
如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
|
||||
*
|
||||
* @param minCapacity 所需的最小容量
|
||||
*/
|
||||
public void ensureCapacity(int minCapacity) {
|
||||
@ -595,146 +841,56 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
ensureExplicitCapacity(minCapacity);
|
||||
}
|
||||
}
|
||||
//得到最小扩容量
|
||||
private void ensureCapacityInternal(int minCapacity) {
|
||||
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
|
||||
// 获取默认的容量和传入参数的较大值
|
||||
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
|
||||
|
||||
```
|
||||
|
||||
**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数**
|
||||
|
||||
我们通过下面的代码实际测试以下这个方法的效果:
|
||||
|
||||
```java
|
||||
public class EnsureCapacityTest {
|
||||
public static void main(String[] args) {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
final int N = 10000000;
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < N; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||
```
|
||||
使用ensureCapacity方法前:2158
|
||||
```
|
||||
|
||||
```java
|
||||
public class EnsureCapacityTest {
|
||||
public static void main(String[] args) {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
final int N = 10000000;
|
||||
list = new ArrayList<Object>();
|
||||
long startTime1 = System.currentTimeMillis();
|
||||
list.ensureCapacity(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
list.add(i);
|
||||
}
|
||||
|
||||
ensureExplicitCapacity(minCapacity);
|
||||
}
|
||||
//判断是否需要扩容,上面两个方法都要调用
|
||||
private void ensureExplicitCapacity(int minCapacity) {
|
||||
modCount++;
|
||||
|
||||
// 如果说minCapacity也就是所需的最小容量大于保存ArrayList数据的数组的长度的话,就需要调用grow(minCapacity)方法扩容。
|
||||
//这个minCapacity到底为多少呢?举个例子在添加元素(add)方法中这个minCapacity的大小就为现在数组的长度加1
|
||||
if (minCapacity - elementData.length > 0)
|
||||
//调用grow方法进行扩容,调用此方法代表已经开始扩容了
|
||||
grow(minCapacity);
|
||||
}
|
||||
|
||||
```
|
||||
```java
|
||||
/**
|
||||
* ArrayList扩容的核心方法。
|
||||
*/
|
||||
private void grow(int minCapacity) {
|
||||
//elementData为保存ArrayList数据的数组
|
||||
///elementData.length求数组长度elementData.size是求数组中的元素个数
|
||||
// oldCapacity为旧容量,newCapacity为新容量
|
||||
int oldCapacity = elementData.length;
|
||||
//将oldCapacity 右移一位,其效果相当于oldCapacity /2,
|
||||
//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
|
||||
int newCapacity = oldCapacity + (oldCapacity >> 1);
|
||||
//然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
|
||||
if (newCapacity - minCapacity < 0)
|
||||
newCapacity = minCapacity;
|
||||
//再检查新容量是否超出了ArrayList所定义的最大容量,
|
||||
//若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,
|
||||
//如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
|
||||
if (newCapacity - MAX_ARRAY_SIZE > 0)
|
||||
newCapacity = hugeCapacity(minCapacity);
|
||||
// minCapacity is usually close to size, so this is a win:
|
||||
elementData = Arrays.copyOf(elementData, newCapacity);
|
||||
}
|
||||
|
||||
```
|
||||
扩容机制代码已经做了详细的解释。另外值得注意的是大家很容易忽略的一个运算符:**移位运算符**
|
||||
**简介**:移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向和填充数字的规则分为三种:<font color="red"><<(左移)</font>、<font color="red">>>(带符号右移)</font>和<font color="red">>>>(无符号右移)</font>。
|
||||
**作用**:**对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源**
|
||||
比如这里:int newCapacity = oldCapacity + (oldCapacity >> 1);
|
||||
右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。
|
||||
|
||||
**另外需要注意的是:**
|
||||
|
||||
1. java 中的**length 属性**是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
|
||||
|
||||
2. java 中的**length()方法**是针对字 符串String说的,如果想看这个字符串的长度则用到 length()这个方法.
|
||||
|
||||
3. .java 中的**size()方法**是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
|
||||
|
||||
|
||||
#### 内部类
|
||||
```java
|
||||
(1)private class Itr implements Iterator<E>
|
||||
(2)private class ListItr extends Itr implements ListIterator<E>
|
||||
(3)private class SubList extends AbstractList<E> implements RandomAccess
|
||||
(4)static final class ArrayListSpliterator<E> implements Spliterator<E>
|
||||
```
|
||||
ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**, **next()**, **remove()** 等方法;其中的**ListItr** 继承 **Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**, **nextIndex()**, **previousIndex()**, **previous()**, **set(E e)**, **add(E e)** 等方法,所以这也可以看出了 **Iterator和ListIterator的区别:** ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。
|
||||
### <font face="楷体" id="6"> ArrayList经典Demo</font>
|
||||
|
||||
```java
|
||||
package list;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ArrayListDemo {
|
||||
|
||||
public static void main(String[] srgs){
|
||||
ArrayList<Integer> arrayList = new ArrayList<Integer>();
|
||||
|
||||
System.out.printf("Before add:arrayList.size() = %d\n",arrayList.size());
|
||||
|
||||
arrayList.add(1);
|
||||
arrayList.add(3);
|
||||
arrayList.add(5);
|
||||
arrayList.add(7);
|
||||
arrayList.add(9);
|
||||
System.out.printf("After add:arrayList.size() = %d\n",arrayList.size());
|
||||
|
||||
System.out.println("Printing elements of arrayList");
|
||||
// 三种遍历方式打印元素
|
||||
// 第一种:通过迭代器遍历
|
||||
System.out.print("通过迭代器遍历:");
|
||||
Iterator<Integer> it = arrayList.iterator();
|
||||
while(it.hasNext()){
|
||||
System.out.print(it.next() + " ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
// 第二种:通过索引值遍历
|
||||
System.out.print("通过索引值遍历:");
|
||||
for(int i = 0; i < arrayList.size(); i++){
|
||||
System.out.print(arrayList.get(i) + " ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
// 第三种:for循环遍历
|
||||
System.out.print("for循环遍历:");
|
||||
for(Integer number : arrayList){
|
||||
System.out.print(number + " ");
|
||||
}
|
||||
|
||||
// toArray用法
|
||||
// 第一种方式(最常用)
|
||||
Integer[] integer = arrayList.toArray(new Integer[0]);
|
||||
|
||||
// 第二种方式(容易理解)
|
||||
Integer[] integer1 = new Integer[arrayList.size()];
|
||||
arrayList.toArray(integer1);
|
||||
|
||||
// 抛出异常,java不支持向下转型
|
||||
//Integer[] integer2 = new Integer[arrayList.size()];
|
||||
//integer2 = arrayList.toArray();
|
||||
System.out.println();
|
||||
|
||||
// 在指定位置添加元素
|
||||
arrayList.add(2,2);
|
||||
// 删除指定位置上的元素
|
||||
arrayList.remove(2);
|
||||
// 删除指定元素
|
||||
arrayList.remove((Object)3);
|
||||
// 判断arrayList是否包含5
|
||||
System.out.println("ArrayList contains 5 is: " + arrayList.contains(5));
|
||||
|
||||
// 清空ArrayList
|
||||
arrayList.clear();
|
||||
// 判断ArrayList是否为空
|
||||
System.out.println("ArrayList is empty: " + arrayList.isEmpty());
|
||||
long endTime1 = System.currentTimeMillis();
|
||||
System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
运行结果:
|
||||
|
||||
```
|
||||
使用ensureCapacity方法前:1773
|
||||
```
|
||||
|
||||
通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。
|
@ -23,8 +23,10 @@ List list=Collections.synchronizedList(new LinkedList(...));
|
||||
```
|
||||
## <font face="楷体" id="2">内部结构分析</font>
|
||||
**如下图所示:**
|
||||

|
||||
|
||||

|
||||
看完了图之后,我们再看LinkedList类中的一个<font color="red">**内部私有类Node**</font>就很好理解了:
|
||||
|
||||
```java
|
||||
private static class Node<E> {
|
||||
E item;//节点值
|
BIN
docs/java/collection/images/linkedlist/LinkedList内部结构.png
Normal file
After Width: | Height: | Size: 18 KiB |
@ -1,416 +0,0 @@
|
||||
> 原文链接:https://www.cnblogs.com/liqiangchn/p/12000361.html
|
||||
|
||||
简洁清爽的代码风格应该是大多数工程师所期待的。在工作中笔者常常因为起名字而纠结,夸张点可以说是编程5分钟,命名两小时!究竟为什么命名成为了工作中的拦路虎。
|
||||
|
||||
每个公司都有不同的标准,目的是为了保持统一,减少沟通成本,提升团队研发效能。所以本文中是笔者结合阿里巴巴开发规范,以及工作中的见闻针对Java领域相关命名进行整理和总结,仅供参考。
|
||||
|
||||
## 一,Java中的命名规范
|
||||
|
||||
好的命名能体现出代码的特征,含义或者是用途,让阅读者可以根据名称的含义快速厘清程序的脉络。不同语言中采用的命名形式大相径庭,Java中常用到的命名形式共有三种,既首字母大写的UpperCamelCase,首字母小写的lowerCamelCase以及全部大写的并用下划线分割单词的UPPER_CAMEL_UNSER_SCORE。通常约定,**类一般采用大驼峰命名,方法和局部变量使用小驼峰命名,而大写下划线命名通常是常量和枚举中使用。**
|
||||
|
||||
| 类型 | 约束 | 例 |
|
||||
| :----: | :----------------------------------------------------------: | :--------------------------------------------: |
|
||||
| 项目名 | 全部小写,多个单词用中划线分隔‘-’ | spring-cloud |
|
||||
| 包名 | 全部小写 | com.alibaba.fastjson |
|
||||
| 类名 | 单词首字母大写 | Feature, ParserConfig,DefaultFieldDeserializer |
|
||||
| 变量名 | 首字母小写,多个单词组成时,除首个单词,其他单词首字母都要大写 | password, userName |
|
||||
| 常量名 | 全部大写,多个单词,用'_'分隔 | CACHE_EXPIRED_TIME |
|
||||
| 方法 | 同变量 | read(), readObject(), getById() |
|
||||
|
||||
## 二,包命名
|
||||
|
||||
**包名**统一使用**小写**,**点分隔符**之间有且仅有一个自然语义的英文单词或者多个单词自然连接到一块(如 springframework,deepspace不需要使用任何分割)。包名统一使用单数形式,如果类命有复数含义,则可以使用复数形式。
|
||||
|
||||
包名的构成可以分为以下几四部分【前缀】 【发起者名】【项目名】【模块名】。常见的前缀可以分为以下几种:
|
||||
|
||||
| 前缀名 | 例 | 含义 |
|
||||
| :-------------: | :----------------------------: | :----------------------------------------------------------: |
|
||||
| indi(或onem ) | indi.发起者名.项目名.模块名.…… | 个体项目,指个人发起,但非自己独自完成的项目,可公开或私有项目,copyright主要属于发起者。 |
|
||||
| pers | pers.个人名.项目名.模块名.…… | 个人项目,指个人发起,独自完成,可分享的项目,copyright主要属于个人 |
|
||||
| priv | priv.个人名.项目名.模块名.…… | 私有项目,指个人发起,独自完成,非公开的私人使用的项目,copyright属于个人。 |
|
||||
| team | team.团队名.项目名.模块名.…… | 团队项目,指由团队发起,并由该团队开发的项目,copyright属于该团队所有 |
|
||||
| 顶级域名 | com.公司名.项目名.模块名.…… | 公司项目,copyright由项目发起的公司所有 |
|
||||
|
||||
## 三,类命名
|
||||
|
||||
**类名使用大驼峰命名形式**,类命通常时**名词或名词短语**,接口名除了用名词和名词短语以外,还可以使用形容词或形容词短语,如Cloneable,Callable等,表示实现该接口的类有某种功能或能力。对于测试类则以它要测试的类开头,以Test结尾,如HashMapTest。
|
||||
|
||||
对于一些特殊特有名词缩写也可以使用全大写命名,比如XMLHttpRequest,不过笔者认为缩写三个字母以内都大写,超过三个字母则按照要给单词算。这个没有标准如阿里巴巴中fastjson用JSONObject作为类命,而google则使用JsonObjectRequest命名,对于这种特殊的缩写,原则是统一就好。
|
||||
|
||||
| 属性 | 约束 | 例 |
|
||||
| -------------- | ----------------------------------------- | ------------------------------------------------------------ |
|
||||
| 抽象类 | Abstract 或者 Base 开头 | BaseUserService |
|
||||
| 枚举类 | Enum 作为后缀 | GenderEnum |
|
||||
| 工具类 | Utils作为后缀 | StringUtils |
|
||||
| 异常类 | Exception结尾 | RuntimeException |
|
||||
| 接口实现类 | 接口名+ Impl | UserServiceImpl |
|
||||
| 领域模型相关 | /DO/DTO/VO/DAO | 正例:UserDAO 反例: UserDo, UserDao |
|
||||
| 设计模式相关类 | Builder,Factory等 | 当使用到设计模式时,需要使用对应的设计模式作为后缀,如ThreadFactory |
|
||||
| 处理特定功能的 | Handler,Predicate, Validator | 表示处理器,校验器,断言,这些类工厂还有配套的方法名如handle,predicate,validate |
|
||||
| 测试类 | Test结尾 | UserServiceTest, 表示用来测试UserService类的 |
|
||||
| MVC分层 | Controller,Service,ServiceImpl,DAO后缀 | UserManageController,UserManageDAO |
|
||||
|
||||
## 四,方法
|
||||
|
||||
**方法命名采用小驼峰的形式**,首字小写,往后的每个单词首字母都要大写。 和类名不同的是,方法命名一般为**动词或动词短语**,与参数或参数名共同组成动宾短语,即动词 + 名词。一个好的函数名一般能通过名字直接获知该函数实现什么样的功能。
|
||||
|
||||
### 4.1 返回真伪值的方法
|
||||
|
||||
注:Prefix-前缀,Suffix-后缀,Alone-单独使用
|
||||
|
||||
| 位置 | 单词 | 意义 | 例 |
|
||||
| ------ | ------ | ------------------------------------------------------------ | ------------- |
|
||||
| Prefix | is | 对象是否符合期待的状态 | isValid |
|
||||
| Prefix | can | 对象**能否执行**所期待的动作 | canRemove |
|
||||
| Prefix | should | 调用方执行某个命令或方法是**好还是不好**,**应不应该**,或者说**推荐还是不推荐** | shouldMigrate |
|
||||
| Prefix | has | 对象**是否持有**所期待的数据和属性 | hasObservers |
|
||||
| Prefix | needs | 调用方**是否需要**执行某个命令或方法 | needsMigrate |
|
||||
|
||||
### 4.2 用来检查的方法
|
||||
|
||||
| 单词 | 意义 | 例 |
|
||||
| -------- | ---------------------------------------------------- | -------------- |
|
||||
| ensure | 检查是否为期待的状态,不是则抛出异常或返回error code | ensureCapacity |
|
||||
| validate | 检查是否为正确的状态,不是则抛出异常或返回error code | validateInputs |
|
||||
|
||||
### 4.3 按需求才执行的方法
|
||||
|
||||
| 位置 | 单词 | 意义 | 例 |
|
||||
| ------ | --------- | ----------------------------------------- | ---------------------- |
|
||||
| Suffix | IfNeeded | 需要的时候执行,不需要的时候什么都不做 | drawIfNeeded |
|
||||
| Prefix | might | 同上 | mightCreate |
|
||||
| Prefix | try | 尝试执行,失败时抛出异常或是返回errorcode | tryCreate |
|
||||
| Suffix | OrDefault | 尝试执行,失败时返回默认值 | getOrDefault |
|
||||
| Suffix | OrElse | 尝试执行、失败时返回实际参数中指定的值 | getOrElse |
|
||||
| Prefix | force | 强制尝试执行。error抛出异常或是返回值 | forceCreate, forceStop |
|
||||
|
||||
### 4.4 异步相关方法
|
||||
|
||||
| 位置 | 单词 | 意义 | 例 |
|
||||
| --------------- | ------------ | -------------------------------------------- | --------------------- |
|
||||
| Prefix | blocking | 线程阻塞方法 | blockingGetUser |
|
||||
| Suffix | InBackground | 执行在后台的线程 | doInBackground |
|
||||
| Suffix | Async | 异步方法 | sendAsync |
|
||||
| Suffix | Sync | 对应已有异步方法的同步方法 | sendSync |
|
||||
| Prefix or Alone | schedule | Job和Task放入队列 | schedule, scheduleJob |
|
||||
| Prefix or Alone | post | 同上 | postJob |
|
||||
| Prefix or Alone | execute | 执行异步方法(注:我一般拿这个做同步方法名) | execute, executeTask |
|
||||
| Prefix or Alone | start | 同上 | start, startJob |
|
||||
| Prefix or Alone | cancel | 停止异步方法 | cancel, cancelJob |
|
||||
| Prefix or Alone | stop | 同上 | stop, stopJob |
|
||||
|
||||
### 4.5 回调方法
|
||||
|
||||
| 位置 | 单词 | 意义 | 例 |
|
||||
| ------ | ------ | -------------------------- | ------------ |
|
||||
| Prefix | on | 事件发生时执行 | onCompleted |
|
||||
| Prefix | before | 事件发生前执行 | beforeUpdate |
|
||||
| Prefix | pre | 同上 | preUpdate |
|
||||
| Prefix | will | 同上 | willUpdate |
|
||||
| Prefix | after | 事件发生后执行 | afterUpdate |
|
||||
| Prefix | post | 同上 | postUpdate |
|
||||
| Prefix | did | 同上 | didUpdate |
|
||||
| Prefix | should | 确认事件是否可以发生时执行 | shouldUpdate |
|
||||
|
||||
### 4.6 操作对象生命周期的方法
|
||||
|
||||
| 单词 | 意义 | 例 |
|
||||
| ---------- | ------------------------------ | --------------- |
|
||||
| initialize | 初始化。也可作为延迟初始化使用 | initialize |
|
||||
| pause | 暂停 | onPause ,pause |
|
||||
| stop | 停止 | onStop,stop |
|
||||
| abandon | 销毁的替代 | abandon |
|
||||
| destroy | 同上 | destroy |
|
||||
| dispose | 同上 | dispose |
|
||||
|
||||
### 4.7 与集合操作相关的方法
|
||||
|
||||
| 单词 | 意义 | 例 |
|
||||
| -------- | ---------------------------- | ---------- |
|
||||
| contains | 是否持有与指定对象相同的对象 | contains |
|
||||
| add | 添加 | addJob |
|
||||
| append | 添加 | appendJob |
|
||||
| insert | 插入到下标n | insertJob |
|
||||
| put | 添加与key对应的元素 | putJob |
|
||||
| remove | 移除元素 | removeJob |
|
||||
| enqueue | 添加到队列的最末位 | enqueueJob |
|
||||
| dequeue | 从队列中头部取出并移除 | dequeueJob |
|
||||
| push | 添加到栈头 | pushJob |
|
||||
| pop | 从栈头取出并移除 | popJob |
|
||||
| peek | 从栈头取出但不移除 | peekJob |
|
||||
| find | 寻找符合条件的某物 | findById |
|
||||
|
||||
### 4.8 与数据相关的方法
|
||||
|
||||
| 单词 | 意义 | 例 |
|
||||
| ------ | -------------------------------------- | ------------- |
|
||||
| create | 新创建 | createAccount |
|
||||
| new | 新创建 | newAccount |
|
||||
| from | 从既有的某物新建,或是从其他的数据新建 | fromConfig |
|
||||
| to | 转换 | toString |
|
||||
| update | 更新既有某物 | updateAccount |
|
||||
| load | 读取 | loadAccount |
|
||||
| fetch | 远程读取 | fetchAccount |
|
||||
| delete | 删除 | deleteAccount |
|
||||
| remove | 删除 | removeAccount |
|
||||
| save | 保存 | saveAccount |
|
||||
| store | 保存 | storeAccount |
|
||||
| commit | 保存 | commitChange |
|
||||
| apply | 保存或应用 | applyChange |
|
||||
| clear | 清除数据或是恢复到初始状态 | clearAll |
|
||||
| reset | 清除数据或是恢复到初始状态 | resetAll |
|
||||
|
||||
### 4.9 成对出现的动词
|
||||
|
||||
| 单词 | 意义 |
|
||||
| -------------- | ----------------- |
|
||||
| get获取 | set 设置 |
|
||||
| add 增加 | remove 删除 |
|
||||
| create 创建 | destory 移除 |
|
||||
| start 启动 | stop 停止 |
|
||||
| open 打开 | close 关闭 |
|
||||
| read 读取 | write 写入 |
|
||||
| load 载入 | save 保存 |
|
||||
| create 创建 | destroy 销毁 |
|
||||
| begin 开始 | end 结束 |
|
||||
| backup 备份 | restore 恢复 |
|
||||
| import 导入 | export 导出 |
|
||||
| split 分割 | merge 合并 |
|
||||
| inject 注入 | extract 提取 |
|
||||
| attach 附着 | detach 脱离 |
|
||||
| bind 绑定 | separate 分离 |
|
||||
| view 查看 | browse 浏览 |
|
||||
| edit 编辑 | modify 修改 |
|
||||
| select 选取 | mark 标记 |
|
||||
| copy 复制 | paste 粘贴 |
|
||||
| undo 撤销 | redo 重做 |
|
||||
| insert 插入 | delete 移除 |
|
||||
| add 加入 | append 添加 |
|
||||
| clean 清理 | clear 清除 |
|
||||
| index 索引 | sort 排序 |
|
||||
| find 查找 | search 搜索 |
|
||||
| increase 增加 | decrease 减少 |
|
||||
| play 播放 | pause 暂停 |
|
||||
| launch 启动 | run 运行 |
|
||||
| compile 编译 | execute 执行 |
|
||||
| debug 调试 | trace 跟踪 |
|
||||
| observe 观察 | listen 监听 |
|
||||
| build 构建 | publish 发布 |
|
||||
| input 输入 | output 输出 |
|
||||
| encode 编码 | decode 解码 |
|
||||
| encrypt 加密 | decrypt 解密 |
|
||||
| compress 压缩 | decompress 解压缩 |
|
||||
| pack 打包 | unpack 解包 |
|
||||
| parse 解析 | emit 生成 |
|
||||
| connect 连接 | disconnect 断开 |
|
||||
| send 发送 | receive 接收 |
|
||||
| download 下载 | upload 上传 |
|
||||
| refresh 刷新 | synchronize 同步 |
|
||||
| update 更新 | revert 复原 |
|
||||
| lock 锁定 | unlock 解锁 |
|
||||
| check out 签出 | check in 签入 |
|
||||
| submit 提交 | commit 交付 |
|
||||
| push 推 | pull 拉 |
|
||||
| expand 展开 | collapse 折叠 |
|
||||
| begin 起始 | end 结束 |
|
||||
| start 开始 | finish 完成 |
|
||||
| enter 进入 | exit 退出 |
|
||||
| abort 放弃 | quit 离开 |
|
||||
| obsolete 废弃 | depreciate 废旧 |
|
||||
| collect 收集 | aggregate 聚集 |
|
||||
|
||||
## 五,变量&常量命名
|
||||
|
||||
### 5.1 变量命名
|
||||
|
||||
变量是指在程序运行中可以改变其值的量,包括成员变量和局部变量。变量名由多单词组成时,第一个单词的首字母小写,其后单词的首字母大写,俗称骆驼式命名法(也称驼峰命名法),如 computedValues,index、变量命名时,尽量简短且能清楚的表达变量的作用,命名体现具体的业务含义即可。
|
||||
|
||||
变量名不应以下划线或美元符号开头,尽管这在语法上是允许的。变量名应简短且富于描述。变量名的选用应该易于记忆,即,能够指出其用途。尽量避免单个字符的变量名,除非是一次性的临时变量。pojo中的布尔变量,都不要加is(数据库中的布尔字段全都要加 is_ 前缀)。
|
||||
|
||||
### 5.2 常量命名
|
||||
|
||||
常量命名CONSTANT_CASE,一般采用全部大写(作为方法参数时除外),单词间用下划线分割。那么什么是常量呢?
|
||||
|
||||
常量是在作用域内保持不变的值,一般使用final进行修饰。一般分为三种,全局常量(public static final修饰),类内常量(private static final 修饰)以及局部常量(方法内,或者参数中的常量),局部常量比较特殊,通常采用小驼峰命名即可。
|
||||
|
||||
```java
|
||||
/**
|
||||
* 一个demo
|
||||
*
|
||||
* @author Jann Lee
|
||||
* @date 2019-12-07 00:25
|
||||
**/
|
||||
public class HelloWorld {
|
||||
|
||||
/**
|
||||
* 局部常量(正例)
|
||||
*/
|
||||
public static final long USER_MESSAGE_CACHE_EXPIRE_TIME = 3600;
|
||||
|
||||
/**
|
||||
* 局部常量(反例,命名不清晰)
|
||||
*/
|
||||
public static final long MESSAGE_CACHE_TIME = 3600;
|
||||
|
||||
/**
|
||||
* 全局常量
|
||||
*/
|
||||
private static final String ERROR_MESSAGE = " error message";
|
||||
|
||||
/**
|
||||
* 成员变量
|
||||
*/
|
||||
private int currentUserId;
|
||||
|
||||
/**
|
||||
* 控制台打印 {@code message} 信息
|
||||
*
|
||||
* @param message 消息体,局部常量
|
||||
*/
|
||||
public void sayHello(final String message){
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
常量一般都有自己的业务含义,**不要害怕长度过长而进行省略或者缩写**。如,用户消息缓存过期时间的表示,那种方式更佳清晰,交给你来评判。
|
||||
|
||||
## 通用命名规则[#](https://www.cnblogs.com/liqiangchn/p/12000361.html#450918152)
|
||||
|
||||
1. 尽量不要使用拼音;杜绝拼音和英文混用。对于一些通用的表示或者难以用英文描述的可以采用拼音,一旦采用拼音就坚决不能和英文混用。
|
||||
正例: BeiJing, HangZhou
|
||||
反例: validateCanShu
|
||||
2. 命名过程中尽量不要出现特殊的字符,常量除外。
|
||||
3. 尽量不要和jdk或者框架中已存在的类重名,也不能使用java中的关键字命名。
|
||||
4. 妙用介词,如for(可以用同音的4代替), to(可用同音的2代替), from, with,of等。
|
||||
如类名采用User4RedisDO,方法名getUserInfoFromRedis,convertJson2Map等。
|
||||
|
||||
## 六,代码注解
|
||||
|
||||
### 6.1 注解的原则
|
||||
|
||||
好的命名增加代码阅读性,代码的命名往往有严格的限制。而注解不同,程序员往往可以自由发挥,单并不意味着可以为所欲为之胡作非为。优雅的注解通常要满足三要素。
|
||||
|
||||
1. Nothing is strange
|
||||
没有注解的代码对于阅读者非常不友好,哪怕代码写的在清除,阅读者至少从心理上会有抵触,更何况代码中往往有许多复杂的逻辑,所以一定要写注解,不仅要记录代码的逻辑,还有说清楚修改的逻辑。
|
||||
2. Less is more
|
||||
从代码维护角度来讲,代码中的注解一定是精华中的精华。合理清晰的命名能让代码易于理解,对于逻辑简单且命名规范,能够清楚表达代码功能的代码不需要注解。滥用注解会增加额外的负担,更何况大部分都是废话。
|
||||
|
||||
```java
|
||||
// 根据id获取信息【废话注解】
|
||||
getMessageById(id)
|
||||
```
|
||||
|
||||
1. Advance with the time
|
||||
注解应该随着代码的变动而改变,注解表达的信息要与代码中完全一致。通常情况下修改代码后一定要修改注解。
|
||||
|
||||
### 6.2 注解格式
|
||||
|
||||
注解大体上可以分为两种,一种是javadoc注解,另一种是简单注解。javadoc注解可以生成JavaAPI为外部用户提供有效的支持javadoc注解通常在使用IDEA,或者Eclipse等开发工具时都可以自动生成,也支持自定义的注解模板,仅需要对对应的字段进行解释。参与同一项目开发的同学,尽量设置成相同的注解模板。
|
||||
|
||||
#### a. 包注解
|
||||
|
||||
包注解在工作中往往比较特殊,通过包注解可以快速知悉当前包下代码是用来实现哪些功能,强烈建议工作中加上,尤其是对于一些比较复杂的包,包注解一般在包的根目录下,名称统一为package-info.java。
|
||||
|
||||
```java
|
||||
/**
|
||||
* 落地也质量检测
|
||||
* 1. 用来解决什么问题
|
||||
* 对广告主投放的广告落地页进行性能检测,模拟不同的系统,如Android,IOS等; 模拟不同的网络:2G,3G,4G,wifi等
|
||||
*
|
||||
* 2. 如何实现
|
||||
* 基于chrome浏览器,用chromedriver驱动浏览器,设置对应的网络,OS参数,获取到浏览器返回结果。
|
||||
*
|
||||
* 注意: 网络环境配置信息{@link cn.mycookies.landingpagecheck.meta.NetWorkSpeedEnum}目前使用是常规速度,可以根据实际情况进行调整
|
||||
*
|
||||
* @author cruder
|
||||
* @time 2019/12/7 20:3 下午
|
||||
*/
|
||||
package cn.mycookies.landingpagecheck;
|
||||
```
|
||||
|
||||
#### b. 类注接
|
||||
|
||||
javadoc注解中,每个类都必须有注解。
|
||||
|
||||
```java
|
||||
/**
|
||||
* Copyright (C), 2019-2020, Jann balabala...
|
||||
*
|
||||
* 类的介绍:这是一个用来做什么事情的类,有哪些功能,用到的技术.....
|
||||
*
|
||||
* @author 类创建者姓名 保持对齐
|
||||
* @date 创建日期 保持对齐
|
||||
* @version 版本号 保持对齐
|
||||
*/
|
||||
```
|
||||
|
||||
#### c. 属性注解
|
||||
|
||||
在每个属性前面必须加上属性注释,通常有以下两种形式,至于怎么选择,你高兴就好,不过一个项目中要保持统一。
|
||||
|
||||
```java
|
||||
/** 提示信息 */
|
||||
private String userName;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
```
|
||||
|
||||
#### d. 方法注释
|
||||
|
||||
在每个方法前面必须加上方法注释,对于方法中的每个参数,以及返回值都要有说明。
|
||||
|
||||
```java
|
||||
/**
|
||||
* 方法的详细说明,能干嘛,怎么实现的,注意事项...
|
||||
*
|
||||
* @param xxx 参数1的使用说明, 能否为null
|
||||
* @return 返回结果的说明, 不同情况下会返回怎样的结果
|
||||
* @throws 异常类型 注明从此类方法中抛出异常的说明
|
||||
*/
|
||||
```
|
||||
|
||||
#### e. 构造方法注释
|
||||
|
||||
在每个构造方法前面必须加上注释,注释模板如下:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 构造方法的详细说明
|
||||
*
|
||||
* @param xxx 参数1的使用说明, 能否为null
|
||||
* @throws 异常类型 注明从此类方法中抛出异常的说明
|
||||
*/
|
||||
```
|
||||
|
||||
而简单注解往往是需要工程师字节定义,在使用注解时应该注意以下几点:
|
||||
|
||||
1. 枚举类的各个属性值都要使用注解,枚举可以理解为是常量,通常不会发生改变,通常会被在多个地方引用,对枚举的修改和添加属性通常会带来很大的影响。
|
||||
2. 保持排版整洁,不要使用行尾注释;双斜杠和星号之后要用1个空格分隔。
|
||||
|
||||
```java
|
||||
id = 1;// 反例:不要使用行尾注释
|
||||
//反例:换行符与注释之间没有缩进
|
||||
int age = 18;
|
||||
// 正例:姓名
|
||||
String name;
|
||||
/**
|
||||
* 1. 多行注释
|
||||
*
|
||||
* 2. 对于不同的逻辑说明,可以用空行分隔
|
||||
*/
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
无论是命名和注解,他们的目的都是为了让代码和工程师进行对话,增强代码的可读性,可维护性。优秀的代码往往能够见名知意,注解往往是对命名的补充和完善。命名太南了!
|
||||
|
||||
参考文献:
|
||||
|
||||
- 《码出高效》
|
||||
- https://www.cnblogs.com/wangcp-2014/p/10215620.html
|
||||
- https://qiita.com/KeithYokoma/items/2193cf79ba76563e3db6
|
||||
- https://google.github.io/styleguide/javaguide.html#s2.1-file-name
|
@ -1,130 +0,0 @@
|
||||
<!-- TOC -->
|
||||
|
||||
- [0.0.1. 泛型的实际应用:实现最小值函数](#001-%e6%b3%9b%e5%9e%8b%e7%9a%84%e5%ae%9e%e9%99%85%e5%ba%94%e7%94%a8%e5%ae%9e%e7%8e%b0%e6%9c%80%e5%b0%8f%e5%80%bc%e5%87%bd%e6%95%b0)
|
||||
- [0.0.2. 使用数组实现栈](#002-%e4%bd%bf%e7%94%a8%e6%95%b0%e7%bb%84%e5%ae%9e%e7%8e%b0%e6%a0%88)
|
||||
- [0.0.3. 实现线程安全的 LRU 缓存](#003-%e5%ae%9e%e7%8e%b0%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
### 0.0.1. 泛型的实际应用:实现最小值函数
|
||||
|
||||
自己设计一个泛型的获取数组最小值的函数.并且这个方法只能接受Number的子类并且实现了Comparable接口。
|
||||
|
||||
```java
|
||||
//注意:Number并没有实现Comparable
|
||||
private static <T extends Number & Comparable<? super T>> T min(T[] values) {
|
||||
if (values == null || values.length == 0) return null;
|
||||
T min = values[0];
|
||||
for (int i = 1; i < values.length; i++) {
|
||||
if (min.compareTo(values[i]) > 0) min = values[i];
|
||||
}
|
||||
return min;
|
||||
}
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
```java
|
||||
int minInteger = min(new Integer[]{1, 2, 3});//result:1
|
||||
double minDouble = min(new Double[]{1.2, 2.2, -1d});//result:-1d
|
||||
String typeError = min(new String[]{"1","3"});//报错
|
||||
```
|
||||
### 0.0.2. 使用数组实现栈
|
||||
|
||||
**自己实现一个栈,要求这个栈具有`push()`、`pop()`(返回栈顶元素并出栈)、`peek()` (返回栈顶元素不出栈)、`isEmpty()`、`size()`这些基本的方法。**
|
||||
|
||||
提示:每次入栈之前先判断栈的容量是否够用,如果不够用就用`Arrays.copyOf()`进行扩容;
|
||||
|
||||
```java
|
||||
public class MyStack {
|
||||
private int[] storage;//存放栈中元素的数组
|
||||
private int capacity;//栈的容量
|
||||
private int count;//栈中元素数量
|
||||
private static final int GROW_FACTOR = 2;
|
||||
|
||||
//不带初始容量的构造方法。默认容量为8
|
||||
public MyStack() {
|
||||
this.capacity = 8;
|
||||
this.storage=new int[8];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
//带初始容量的构造方法
|
||||
public MyStack(int initialCapacity) {
|
||||
if (initialCapacity < 1)
|
||||
throw new IllegalArgumentException("Capacity too small.");
|
||||
|
||||
this.capacity = initialCapacity;
|
||||
this.storage = new int[initialCapacity];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
//入栈
|
||||
public void push(int value) {
|
||||
if (count == capacity) {
|
||||
ensureCapacity();
|
||||
}
|
||||
storage[count++] = value;
|
||||
}
|
||||
|
||||
//确保容量大小
|
||||
private void ensureCapacity() {
|
||||
int newCapacity = capacity * GROW_FACTOR;
|
||||
storage = Arrays.copyOf(storage, newCapacity);
|
||||
capacity = newCapacity;
|
||||
}
|
||||
|
||||
//返回栈顶元素并出栈
|
||||
private int pop() {
|
||||
if (count == 0)
|
||||
throw new IllegalArgumentException("Stack is empty.");
|
||||
count--;
|
||||
return storage[count];
|
||||
}
|
||||
|
||||
//返回栈顶元素不出栈
|
||||
private int peek() {
|
||||
if (count == 0){
|
||||
throw new IllegalArgumentException("Stack is empty.");
|
||||
}else {
|
||||
return storage[count-1];
|
||||
}
|
||||
}
|
||||
|
||||
//判断栈是否为空
|
||||
private boolean isEmpty() {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
//返回栈中元素的个数
|
||||
private int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
验证
|
||||
|
||||
```java
|
||||
MyStack myStack = new MyStack(3);
|
||||
myStack.push(1);
|
||||
myStack.push(2);
|
||||
myStack.push(3);
|
||||
myStack.push(4);
|
||||
myStack.push(5);
|
||||
myStack.push(6);
|
||||
myStack.push(7);
|
||||
myStack.push(8);
|
||||
System.out.println(myStack.peek());//8
|
||||
System.out.println(myStack.size());//8
|
||||
for (int i = 0; i < 8; i++) {
|
||||
System.out.println(myStack.pop());
|
||||
}
|
||||
System.out.println(myStack.isEmpty());//true
|
||||
myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty.
|
||||
```
|
||||
|
||||
|
||||
|
@ -1,441 +0,0 @@
|
||||
<!-- TOC -->
|
||||
|
||||
- [1. LRU 缓存介绍](#1-lru-%e7%bc%93%e5%ad%98%e4%bb%8b%e7%bb%8d)
|
||||
- [2. ConcurrentLinkedQueue简单介绍](#2-concurrentlinkedqueue%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
|
||||
- [3. ReadWriteLock简单介绍](#3-readwritelock%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
|
||||
- [4. ScheduledExecutorService 简单介绍](#4-scheduledexecutorservice-%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d)
|
||||
- [5. 徒手撸一个线程安全的 LRU 缓存](#5-%e5%be%92%e6%89%8b%e6%92%b8%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
|
||||
- [5.1. 实现方法](#51-%e5%ae%9e%e7%8e%b0%e6%96%b9%e6%b3%95)
|
||||
- [5.2. 原理](#52-%e5%8e%9f%e7%90%86)
|
||||
- [5.3. put方法具体流程分析](#53-put%e6%96%b9%e6%b3%95%e5%85%b7%e4%bd%93%e6%b5%81%e7%a8%8b%e5%88%86%e6%9e%90)
|
||||
- [5.4. 源码](#54-%e6%ba%90%e7%a0%81)
|
||||
- [6. 实现一个线程安全并且带有过期时间的 LRU 缓存](#6-%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e5%b9%b6%e4%b8%94%e5%b8%a6%e6%9c%89%e8%bf%87%e6%9c%9f%e6%97%b6%e9%97%b4%e7%9a%84-lru-%e7%bc%93%e5%ad%98)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
最近被读者问到“不用LinkedHashMap的话,如何实现一个线程安全的 LRU 缓存?网上的代码太杂太乱,Guide哥哥能不能帮忙写一个?”。
|
||||
|
||||
*划重点,手写一个 LRU 缓存在面试中还是挺常见的!*
|
||||
|
||||
很多人就会问了:“网上已经有这么多现成的缓存了!为什么面试官还要我们自己实现一个呢?” 。咳咳咳,当然是为了面试需要。哈哈!开个玩笑,我个人觉得更多地是为了学习吧!今天Guide哥教大家:
|
||||
|
||||
1. 实现一个线程安全的 LRU 缓存
|
||||
2. 实现一个线程安全并且带有过期时间的 LRU 缓存
|
||||
|
||||
考虑到了线程安全性我们使用了 `ConcurrentHashMap` 、`ConcurrentLinkedQueue` 这两个线程安全的集合。另外,还用到 `ReadWriteLock`(读写锁)。为了实现带有过期时间的缓存,我们用到了 `ScheduledExecutorService`来做定时任务执行。
|
||||
|
||||
如果有任何不对或者需要完善的地方,请帮忙指出!
|
||||
|
||||
### 1. LRU 缓存介绍
|
||||
|
||||
**LRU (Least Recently Used,最近最少使用)是一种缓存淘汰策略。**
|
||||
|
||||
LRU缓存指的是当缓存大小已达到最大分配容量的时候,如果再要去缓存新的对象数据的话,就需要将缓存中最近访问最少的对象删除掉以便给新来的数据腾出空间。
|
||||
|
||||
### 2. ConcurrentLinkedQueue简单介绍
|
||||
|
||||
**ConcurrentLinkedQueue是一个基于单向链表的无界无锁线程安全的队列,适合在高并发环境下使用,效率比较高。** 我们在使用的时候,可以就把它理解为我们经常接触的数据结构——队列,不过是增加了多线程下的安全性保证罢了。**和普通队列一样,它也是按照先进先出(FIFO)的规则对接点进行排序。** 另外,队列元素中不可以放置null元素。
|
||||
|
||||
`ConcurrentLinkedQueue` 整个继承关系如下图所示:
|
||||
|
||||

|
||||
|
||||
`ConcurrentLinkedQueue中`最主要的两个方法是:`offer(value)`和`poll()`,分别实现队列的两个重要的操作:入队和出队(`offer(value)`等价于 `add(value)`)。
|
||||
|
||||
我们添加一个元素到队列的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。
|
||||
|
||||

|
||||
|
||||
利用`ConcurrentLinkedQueue`队列先进先出的特性,每当我们 `put`/`get`(缓存被使用)元素的时候,我们就将这个元素存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。
|
||||
|
||||
### 3. ReadWriteLock简单介绍
|
||||
|
||||
`ReadWriteLock` 是一个接口,位于`java.util.concurrent.locks`包下,里面只有两个方法分别返回读锁和写锁:
|
||||
|
||||
```java
|
||||
public interface ReadWriteLock {
|
||||
/**
|
||||
* 返回读锁
|
||||
*/
|
||||
Lock readLock();
|
||||
|
||||
/**
|
||||
* 返回写锁
|
||||
*/
|
||||
Lock writeLock();
|
||||
}
|
||||
```
|
||||
|
||||
`ReentrantReadWriteLock` 是`ReadWriteLock`接口的具体实现类。
|
||||
|
||||
**读写锁还是比较适合缓存这种读多写少的场景。读写锁可以保证多个线程和同时读取,但是只有一个线程可以写入。**
|
||||
|
||||
读写锁的特点是:写锁和写锁互斥,读锁和写锁互斥,读锁之间不互斥。也就说:同一时刻只能有一个线程写,但是可以有多个线程
|
||||
读。读写之间是互斥的,两者不能同时发生(当进行写操作时,同一时刻其他线程的读操作会被阻塞;当进行读操作时,同一时刻所有线程的写操作会被阻塞)。
|
||||
|
||||
另外,**同一个线程持有写锁时是可以申请读锁,但是持有读锁的情况下不可以申请写锁。**
|
||||
|
||||
### 4. ScheduledExecutorService 简单介绍
|
||||
|
||||
`ScheduledExecutorService` 是一个接口,`ScheduledThreadPoolExecutor` 是其主要实现类。
|
||||
|
||||

|
||||
|
||||
**`ScheduledThreadPoolExecutor` 主要用来在给定的延迟后运行任务,或者定期执行任务。** 这个在实际项目用到的比较少,因为有其他方案选择比如`quartz`。但是,在一些需求比较简单的场景下还是非常有用的!
|
||||
|
||||
**`ScheduledThreadPoolExecutor` 使用的任务队列 `DelayQueue` 封装了一个 `PriorityQueue`,`PriorityQueue` 会对队列中的任务进行排序,执行所需时间短的放在前面先被执行,如果执行所需时间相同则先提交的任务将被先执行。**
|
||||
|
||||
### 5. 徒手撸一个线程安全的 LRU 缓存
|
||||
|
||||
#### 5.1. 实现方法
|
||||
|
||||
`ConcurrentHashMap` + `ConcurrentLinkedQueue` +`ReadWriteLock`
|
||||
|
||||
#### 5.2. 原理
|
||||
|
||||
`ConcurrentHashMap` 是线程安全的Map,我们可以利用它缓存 key,value形式的数据。`ConcurrentLinkedQueue`是一个线程安全的基于链表的队列(先进先出),我们可以用它来维护 key 。每当我们put/get(缓存被使用)元素的时候,我们就将这个元素对应的 key 存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。当我们的缓存容量不够的时候,我们直接移除队列头部对应的key以及这个key对应的缓存即可!
|
||||
|
||||
另外,我们用到了`ReadWriteLock`(读写锁)来保证线程安全。
|
||||
|
||||
#### 5.3. put方法具体流程分析
|
||||
|
||||
为了方便大家理解,我将代码中比较重要的 `put(key,value)`方法的原理图画了出来,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 5.4. 源码
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author shuang.kou
|
||||
* <p>
|
||||
* 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock实现线程安全的 LRU 缓存
|
||||
* 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache
|
||||
*/
|
||||
public class MyLruCache<K, V> {
|
||||
|
||||
/**
|
||||
* 缓存的最大容量
|
||||
*/
|
||||
private final int maxCapacity;
|
||||
|
||||
private ConcurrentHashMap<K, V> cacheMap;
|
||||
private ConcurrentLinkedQueue<K> keys;
|
||||
/**
|
||||
* 读写锁
|
||||
*/
|
||||
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
private Lock writeLock = readWriteLock.writeLock();
|
||||
private Lock readLock = readWriteLock.readLock();
|
||||
|
||||
public MyLruCache(int maxCapacity) {
|
||||
if (maxCapacity < 0) {
|
||||
throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
|
||||
}
|
||||
this.maxCapacity = maxCapacity;
|
||||
cacheMap = new ConcurrentHashMap<>(maxCapacity);
|
||||
keys = new ConcurrentLinkedQueue<>();
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
// 加写锁
|
||||
writeLock.lock();
|
||||
try {
|
||||
//1.key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
moveToTailOfQueue(key);
|
||||
cacheMap.put(key, value);
|
||||
return value;
|
||||
}
|
||||
//2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存
|
||||
if (cacheMap.size() == maxCapacity) {
|
||||
System.out.println("maxCapacity of cache reached");
|
||||
removeOldestKey();
|
||||
}
|
||||
//3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素
|
||||
keys.add(key);
|
||||
cacheMap.put(key, value);
|
||||
return value;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
//加读锁
|
||||
readLock.lock();
|
||||
try {
|
||||
//key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
// 存在的话就将key移动到队列的尾部
|
||||
moveToTailOfQueue(key);
|
||||
return cacheMap.get(key);
|
||||
}
|
||||
//不存在于当前缓存中就返回Null
|
||||
return null;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public V remove(K key) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
//key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
// 存在移除队列和Map中对应的Key
|
||||
keys.remove(key);
|
||||
return cacheMap.remove(key);
|
||||
}
|
||||
//不存在于当前缓存中就返回Null
|
||||
return null;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素添加到队列的尾部(put/get的时候执行)
|
||||
*/
|
||||
private void moveToTailOfQueue(K key) {
|
||||
keys.remove(key);
|
||||
keys.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行)
|
||||
*/
|
||||
private void removeOldestKey() {
|
||||
K oldestKey = keys.poll();
|
||||
if (oldestKey != null) {
|
||||
cacheMap.remove(oldestKey);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return cacheMap.size();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**非并发环境测试:**
|
||||
|
||||
```java
|
||||
MyLruCache<Integer, String> myLruCache = new MyLruCache<>(3);
|
||||
myLruCache.put(1, "Java");
|
||||
System.out.println(myLruCache.get(1));// Java
|
||||
myLruCache.remove(1);
|
||||
System.out.println(myLruCache.get(1));// null
|
||||
myLruCache.put(2, "C++");
|
||||
myLruCache.put(3, "Python");
|
||||
System.out.println(myLruCache.get(2));//C++
|
||||
myLruCache.put(4, "C");
|
||||
myLruCache.put(5, "PHP");
|
||||
System.out.println(myLruCache.get(2));// C++
|
||||
```
|
||||
|
||||
**并发环境测试:**
|
||||
|
||||
我们初始化了一个固定容量为 10 的线程池和count为10的`CountDownLatch`。我们将1000000次操作分10次添加到线程池,然后我们等待线程池执行完成这10次操作。
|
||||
|
||||
|
||||
```java
|
||||
int threadNum = 10;
|
||||
int batchSize = 100000;
|
||||
//init cache
|
||||
MyLruCache<String, Integer> myLruCache = new MyLruCache<>(batchSize * 10);
|
||||
//init thread pool with 10 threads
|
||||
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(threadNum);
|
||||
//init CountDownLatch with 10 count
|
||||
CountDownLatch latch = new CountDownLatch(threadNum);
|
||||
AtomicInteger atomicInteger = new AtomicInteger(0);
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int t = 0; t < threadNum; t++) {
|
||||
fixedThreadPool.submit(() -> {
|
||||
for (int i = 0; i < batchSize; i++) {
|
||||
int value = atomicInteger.incrementAndGet();
|
||||
myLruCache.put("id" + value, value);
|
||||
}
|
||||
latch.countDown();
|
||||
});
|
||||
}
|
||||
//wait for 10 threads to complete the task
|
||||
latch.await();
|
||||
fixedThreadPool.shutdown();
|
||||
System.out.println("Cache size:" + myLruCache.size());//Cache size:1000000
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
System.out.println(String.format("Time cost:%dms", duration));//Time cost:511ms
|
||||
```
|
||||
|
||||
### 6. 实现一个线程安全并且带有过期时间的 LRU 缓存
|
||||
|
||||
实际上就是在我们上面时间的LRU缓存的基础上加上一个定时任务去删除缓存,单纯利用 JDK 提供的类,我们实现定时任务的方式有很多种:
|
||||
|
||||
1. `Timer` :不被推荐,多线程会存在问题。
|
||||
2. `ScheduledExecutorService` :定时器线程池,可以用来替代 `Timer`
|
||||
3. `DelayQueue` :延时队列
|
||||
4. `quartz` :一个很火的开源任务调度框架,很多其他框架都是基于 `quartz` 开发的,比如当当网的`elastic-job `就是基于`quartz`二次开发之后的分布式调度解决方案
|
||||
5. ......
|
||||
|
||||
最终我们选择了 `ScheduledExecutorService`,主要原因是它易用(基于`DelayQueue`做了很多封装)并且基本能满足我们的大部分需求。
|
||||
|
||||
我们在我们上面实现的线程安全的 LRU 缓存基础上,简单稍作修改即可!我们增加了一个方法:
|
||||
|
||||
```java
|
||||
private void removeAfterExpireTime(K key, long expireTime) {
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
//过期后清除该键值对
|
||||
cacheMap.remove(key);
|
||||
keys.remove(key);
|
||||
}, expireTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
```
|
||||
我们put元素的时候,如果通过这个方法就能直接设置过期时间。
|
||||
|
||||
|
||||
**完整源码如下:**
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author shuang.kou
|
||||
* <p>
|
||||
* 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock+ScheduledExecutorService实现线程安全的 LRU 缓存
|
||||
* 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache
|
||||
*/
|
||||
public class MyLruCacheWithExpireTime<K, V> {
|
||||
|
||||
/**
|
||||
* 缓存的最大容量
|
||||
*/
|
||||
private final int maxCapacity;
|
||||
|
||||
private ConcurrentHashMap<K, V> cacheMap;
|
||||
private ConcurrentLinkedQueue<K> keys;
|
||||
/**
|
||||
* 读写锁
|
||||
*/
|
||||
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
private Lock writeLock = readWriteLock.writeLock();
|
||||
private Lock readLock = readWriteLock.readLock();
|
||||
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
public MyLruCacheWithExpireTime(int maxCapacity) {
|
||||
if (maxCapacity < 0) {
|
||||
throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
|
||||
}
|
||||
this.maxCapacity = maxCapacity;
|
||||
cacheMap = new ConcurrentHashMap<>(maxCapacity);
|
||||
keys = new ConcurrentLinkedQueue<>();
|
||||
scheduledExecutorService = Executors.newScheduledThreadPool(3);
|
||||
}
|
||||
|
||||
public V put(K key, V value, long expireTime) {
|
||||
// 加写锁
|
||||
writeLock.lock();
|
||||
try {
|
||||
//1.key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
moveToTailOfQueue(key);
|
||||
cacheMap.put(key, value);
|
||||
return value;
|
||||
}
|
||||
//2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存
|
||||
if (cacheMap.size() == maxCapacity) {
|
||||
System.out.println("maxCapacity of cache reached");
|
||||
removeOldestKey();
|
||||
}
|
||||
//3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素
|
||||
keys.add(key);
|
||||
cacheMap.put(key, value);
|
||||
if (expireTime > 0) {
|
||||
removeAfterExpireTime(key, expireTime);
|
||||
}
|
||||
return value;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public V get(K key) {
|
||||
//加读锁
|
||||
readLock.lock();
|
||||
try {
|
||||
//key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
// 存在的话就将key移动到队列的尾部
|
||||
moveToTailOfQueue(key);
|
||||
return cacheMap.get(key);
|
||||
}
|
||||
//不存在于当前缓存中就返回Null
|
||||
return null;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public V remove(K key) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
//key是否存在于当前缓存
|
||||
if (cacheMap.containsKey(key)) {
|
||||
// 存在移除队列和Map中对应的Key
|
||||
keys.remove(key);
|
||||
return cacheMap.remove(key);
|
||||
}
|
||||
//不存在于当前缓存中就返回Null
|
||||
return null;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素添加到队列的尾部(put/get的时候执行)
|
||||
*/
|
||||
private void moveToTailOfQueue(K key) {
|
||||
keys.remove(key);
|
||||
keys.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行)
|
||||
*/
|
||||
private void removeOldestKey() {
|
||||
K oldestKey = keys.poll();
|
||||
if (oldestKey != null) {
|
||||
cacheMap.remove(oldestKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeAfterExpireTime(K key, long expireTime) {
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
//过期后清除该键值对
|
||||
cacheMap.remove(key);
|
||||
keys.remove(key);
|
||||
}, expireTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return cacheMap.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**测试效果:**
|
||||
|
||||
```java
|
||||
MyLruCacheWithExpireTime<Integer,String> myLruCache = new MyLruCacheWithExpireTime<>(3);
|
||||
myLruCache.put(1,"Java",3000);
|
||||
myLruCache.put(2,"C++",3000);
|
||||
myLruCache.put(3,"Python",1500);
|
||||
System.out.println(myLruCache.size());//3
|
||||
Thread.sleep(2000);
|
||||
System.out.println(myLruCache.size());//2
|
||||
```
|
@ -1,5 +1,3 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [JDK 监控和故障处理工具总结](#jdk-监控和故障处理工具总结)
|
||||
@ -325,13 +323,3 @@ VisualVM 提供在 Java 虚拟机 (Java Virutal Machine, JVM) 上运行的 Java
|
||||
|
||||
- <https://visualvm.github.io/documentation.html>
|
||||
- <https://www.ibm.com/developerworks/cn/java/j-lo-visualvm/index.html>
|
||||
|
||||
## 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本公众号后台回复 **"Java面试突击"** 即可免费领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
@ -1,5 +1,3 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [JVM 垃圾回收](#jvm-垃圾回收)
|
||||
@ -334,7 +332,7 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
|
||||
|
||||
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
|
||||
|
||||
<img src="./pictures/jvm垃圾回收/90984624.png" alt="公众号" width="500px">
|
||||

|
||||
|
||||
### 3.3 标记-整理算法
|
||||
|
||||
@ -401,7 +399,7 @@ Parallel Scavenge 收集器也是使用复制算法的多线程收集器,它
|
||||
|
||||
```
|
||||
|
||||
**Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。** Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,手工优化存在困难的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。
|
||||
**Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。** Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理优化交给虚拟机去完成也是一个不错的选择。
|
||||
|
||||
**新生代采用复制算法,老年代采用标记-整理算法。**
|
||||

|
||||
@ -473,16 +471,6 @@ G1 收集器的运作大致分为以下几个步骤:
|
||||
- https://my.oschina.net/hosee/blog/644618
|
||||
- <https://docs.oracle.com/javase/specs/jvms/se8/html/index.html>
|
||||
|
||||
## 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [Java 内存区域详解](#java-内存区域详解)
|
||||
@ -492,13 +490,3 @@ i4=i5+i6 true
|
||||
- <https://dzone.com/articles/jvm-permgen-%E2%80%93-where-art-thou>
|
||||
- <https://stackoverflow.com/questions/9095748/method-area-and-permgen>
|
||||
- 深入解析String#intern<https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html>
|
||||
|
||||
## 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
@ -1,5 +1,3 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [回顾一下类加载过程](#回顾一下类加载过程)
|
||||
@ -134,13 +132,5 @@ protected Class<?> loadClass(String name, boolean resolve)
|
||||
- <https://juejin.im/post/5c04892351882516e70dcc9b>
|
||||
- <http://gityuan.com/2016/01/24/java-classloader/>
|
||||
|
||||
### 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
||||
|
||||
> 公众号JavaGuide 后台回复关键字“1”,免费获取JavaGuide配套的Java工程师必备学习资源(文末有公众号二维码)。
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [类的生命周期](#类的生命周期)
|
||||
|
@ -212,13 +212,3 @@ Class 文件存储格式中对方法的描述与对字段的描述几乎采用
|
||||
- <https://coolshell.cn/articles/9229.html>
|
||||
- <https://blog.csdn.net/luanlouis/article/details/39960815>
|
||||
- 《实战 Java 虚拟机》
|
||||
|
||||
## 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 23 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/Executors工具类.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/Executor框架的使用示意图.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/FixedThreadPool.png
Normal file
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 28 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/任务的执行相关接口.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/图解线程池实现原理.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/java/multi-thread/images/java线程池学习总结/线程池各个参数之间的关系.png
Normal file
After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |