• String.split()方法你可能不知道的一面


      String.split()方法你可能不知道的一面
      一、问题
      java中String的split()是我们经常使用的方法,用来按照特定字符分割字符串,那么我们看以下一段代码:
      public void splitTest() {
      String str = "aaa|bbb|ccc";
      String[] array = str.split("|");
      System.out.println(Arrays.toString(array));
      }
      是不是感觉很简单,就是吧str按照"|"分割,结果就是[aaa,bbb,ccc]嘛。如果你这么想,那么以后在用这个方法时你可能会犯下大错,把程序跑起来,你会惊讶的发现程序输入如下结果:
      [, a, a, a, |, b, b, b, |, c, c, c]
      为什么会出现这种情况呢?我们再来运行一下str.split("");就会发现结果和之前的结果一样,也就是说我们使用"|"分割的时候其实split是按照空字符分割的。
      二、探究
      为了找到原因,我们在来使用其他几种字符测试一下,结果如下:
      0:[aaa, bbb, ccc]
      $:[aaa$bbb$ccc]
      ,:[aaa, bbb, ccc]
      *:异常java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0
      ^:[aaa^bbb^ccc]
      我们会发现对于数字,","结果是正确的,而对于特殊字符,结果都不正确,而且如果经验丰富,你可能已经发现,这些特殊字符都是正则表达式中的匹配符,而那个异常也很明确的说明了这一点。
      因此,我们可以猜测在split内部使用了正则表达式来匹配并分割字符串。那么现在又有一个疑问:我们知道一般情况下String涉及的字符串处理多数使用indexOf()(可能是考虑效率问题吧),那么这里为什么使用正则呢?我们还是直接看看源码吧:
      public String[] split(String regex, int limit) {
      char ch = 0;
      if (((regex.value.length == 1 &&
      ".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||
      (regex.length() == 2 &&
      regex.charAt(0) == '\' &&
      (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
      ((ch-'a')|('z'-ch)) < 0 &&
      ((ch-'A')|('Z'-ch)) < 0)) &&
      (ch < Character.MIN_HIGH_SURROGATE ||
      ch > Character.MAX_LOW_SURROGATE))
      {
      int off = 0;
      int next = 0;
      boolean limited = limit > 0;
      ArrayList<String> list = new ArrayList<>();
      while ((next = indexOf(ch, off)) != -1) {
      if (!limited || list.size() < limit - 1) {
      list.add(substring(off, next));
      off = next + 1;
      } else { // last one
      //assert (list.size() == limit - 1);
      list.add(substring(off, value.length));
      off = value.length;
      break;
      }
      }
      // If no match was found, return this
      if (off == 0)
      return new String[]{this};
      // Add remaining segment
      if (!limited || list.size() < limit)
      list.add(substring(off, value.length));
      // Construct result
      int resultSize = list.size();
      if (limit == 0)
      while (resultSize > 0 && list.get(resultSize - 1)。length() == 0)
      resultSize--;
      String[] result = new String[resultSize];
      return list.subList(0, resultSize)。toArray(result);
      }
      return Pattern.compile(regex)。split(this, limit);
      }
      可以看到方法内有一个if,如果条件为true,那么就使用indexOf()判断后substring()截取,如果为false,则使用正则处理。那么我们就来分析下这个if的条件:
      //第一步部分:当regex的长度为1且不是".$|()[{^?*+\"中的时,为真
      (regex.value.length == 1 &&".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1)
      //第二部分:当长度为2时且第一个字符为""转义字符,第二个字符不是字符0-9 a-z A-Z 以及utf-16之间的字符
      (regex.length() == 2 && regex.charAt(0) == '\' && (((
      ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
      ((ch-'a')|('z'-ch)) < 0 &&
      ((ch-'A')|('Z'-ch)) < 0)) &&
      (ch < Character.MIN_HIGH_SURROGATE ||
      ch > Character.MAX_LOW_SURROGATE) www.tygj123.com
      从if可以看出如果regex内容为一个非正则匹配符或者是转以后的特殊字符时,采用indexOf()+substring()处理,否则使用正则表达式。
      那么为什么这么做呢,直接使用第一种方法不就行了?其实我们可以考虑一种复杂的情况:
      aaax111xbbbx222xcccx333xddd
      如果我想分割出这样的结果 [aaa, bbb, ccc, ddd] 应该则么做呢?按照第一种方法,实现起来很麻烦,需要一大堆判断,反而不如正则方便,而String中的split()方法正是出于这样的考虑实现的,不信你用split("x.*?x")试试。
      三、启示
      这次这件事虽小,但是却让我收获不少。
      1、使用一个不了解的方法前,一定要看一眼提示,split方法说明里虽然没有明确的提示上述问题,但是多个地方都提到了正则,连参数名字都是regex,作为一个经验丰富的程序员,应该想到这一点。
      2、对于一些小的功能点(往往我们还胸有成竹)一定要写个Test测一下,其实把代码贴过去跑一下费不了多少劲,但是当他淹没在成百上千行的代码中时,想要在发现问题就很费劲了,特别是你不调试根本想不到这点会错 www.yztrans.com
      3、我们在多数项目中都要建个util工具包,封装各种辅助的操作类,然而这些类的功能往往只是满足当时的需求,之后用到类似但有变动的功能时往往习惯单独实现一个,而不是实现一个完善的覆盖原来的,从设计的角度会造成接口不一致:明明同样的需求、同样的参数,我却还要看看他的内部实现才能选择用哪个。对于这些工具类,我们不一定要非常完美,但是应该用心做到尽量完善,这也方便复用。

  • 相关阅读:
    HDU 1385 Minimum Transport Cost
    TOJ 3488 Game Dice
    TOJ 1717 WOJ
    POJ 2553 The Bottom of a Graph
    TOJ 1836 Play on Words
    利用OpenCV建立视差图像
    分享一个PyTorch医学图像分割开源库
    WACV 2021 论文大盘点-图像分割篇
    SolidWorks动画教程(1):简单动画制作
    VS2013安装及破解教程
  • 原文地址:https://www.cnblogs.com/haosola/p/3655733.html
Copyright © 2020-2023  润新知