前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的SimpeDateFormatter优化

Java中的SimpeDateFormatter优化

作者头像
None_Ling
发布2021-05-24 16:17:59
4450
发布2021-05-24 16:17:59
举报
文章被收录于专栏:Android相关

背景

遇到要在本地解析一套文件系统,其中有以下特征:

  • 每个文件预计20M,每个文件约10W行数据
  • 日志按日期进行分类,同时按时间顺序增加
  • 每行都会有[2021-05-14 12:01:19.195]这种时间

原来是通过正则表达式以及SimpleDateFormatter.parse()进行解析,从而导致解析一个文件耗时非常长.

优化前每个文件预计在80s左右,在优化完后,每个文件只需要花费9s左右即可完成。

优化手段

1. 尽量不要使用Pattern

由于正则表达式会在遍历字符串的时候进行回溯导致匹配之间过长。

优化方案:

  • 如果有标准格式或者分隔符,例如[...]....#....则尽量使用字符遍历以及StringBuilder.append来组合字符串。
2. 尽量少使用SimpleDateFormatter.parse()计算时间

如果一个文件中如果出现大量的日期需要转换成时间戳,例如:[2021-05-14 12:01:19.195]转换成1620964879195的话,尽量不要直接通过SimpleDateFormatter.parse().time来转时间。

优化方案:

  • 通过SimpleDateFormatter.parse("yyy-mm-dd")将当天的时间戳计算,并且将转换结果缓存起来
  • 通过字符匹配,以及char - '0'来转换成数字,通过乘法来得到具体的数值。例如12的数值可通过:('1'-'0')*10 + ('2'-'0') = 12来获得
  • 最后通过解析的日期、十分秒、毫秒相加,得到当前时间戳。

由于每年的日期都不确定,所以需要通过SimpleDateFormatter.parse("yyy-mm-dd").time来解析某一天的时间戳。

代码语言:javascript
复制
        // 时间解析阶段
        const val PARSE_STAGE_HOUR = 0
        const val PARSE_STAGE_MINUTE = 1
        const val PARSE_STAGE_SECONDS = 2
        const val MILESECONDS_HOUR = 3600000
        const val MILESECONDS_MINUTE = 60000
        const val MILESECONDS_SECOND = 1000
        const val SYMBOL_ASCII_0 = '0'
        const val SYMBOL_COLON = ':'
        const val SYMBOL_DOT = '.'
        // 用于计算当前日期的时间戳
        val dayFormatter = SimpleDateFormat("yyyy-MM-dd")
        // 用于保存日期对应的时间戳
        val dayMap = mutableMapOf<String, Long>()

private fun parseLogTime(timeStr: String) {
        // 解析[2021-05-07 +80 16:08:07.155]的时间戳
        val array = timeStr.toCharArray()
        // 解析到2021-05-07的时间戳
        val day = String(array, 0, PARSE_OFFSET_DAY)
        var dayTimeStamp = dayMap[day] ?: 0L
        if (dayTimeStamp == 0L) {
            // 如果没有缓存,则需要解析并且保存
            dayTimeStamp = dayFormatter.parse(day).time
            dayMap[day] = dayTimeStamp
        }
        // 开始解析16:08:07.155的时间戳
        val length = array.size
        val index = length - PARSE_OFFSET_HOUR
        var hour = 0
        var minutes = 0
        var seconds = 0
        // 解析HOUR阶段
        var stage = PARSE_STAGE_HOUR
        var num = 0
        for (i in index until length) {
            // 从16:08:07.155开始遍历字符
            val c = array[i]
            if (c == SYMBOL_COLON || c == SYMBOL_DOT) {
                // 如果遇到:或者.,则进入下一个阶段,保存后将num清0
                when (stage) {
                    PARSE_STAGE_HOUR -> hour = num
                    PARSE_STAGE_MINUTE -> minutes = num
                    PARSE_STAGE_SECONDS -> seconds = num
                }
                stage++
                num = 0
            } else {
                num = (num * 10) + (c - SYMBOL_ASCII_0)
            }
        }
        // 最后解析完的num就是mileseconds
        timeStamp = dayTimeStamp + hour * MILESECONDS_HOUR +
                minutes * MILESECONDS_MINUTE + seconds * MILESECONDS_SECOND + num
    }
3. 尽量不要使用subString等函数进行字符串分割

subString来截取1240等等数值效率非常低下。

优化方案:

  • 尽量使用字符串遍历,得到起始与结束的偏移量,通过String(charArray,offset , length)来构建字符串,效率更高
4. SimpleDateFormatter是非线程安全的

SimpleDateFormatter是非线程安全的,需要自己做同步

优化方案 :

  • 尽量使用ThreadLocal保存SimpleDateFormatter对象
  • 创建SimpleDateFormatter非常耗时,尽量在单个线程中初始化一个。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 优化手段
    • 1. 尽量不要使用Pattern
      • 2. 尽量少使用SimpleDateFormatter.parse()计算时间
        • 3. 尽量不要使用subString等函数进行字符串分割
          • 4. SimpleDateFormatter是非线程安全的
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档