• StringUtils # split 的坑


    背景

    public static void main(String[] args) {
        String netmask = "103.140.147.0/24";
        System.out.println(org.springframework.util.StringUtils.split(netmask, ","));
        System.out.println(org.apache.commons.lang3.StringUtils.split(netmask, ","));
    }
    
    

    有这么一段常见的代码,可以想象结果吗? 今天在项目测试中就出现了 误用StringUtils.split. 方法导致的 NullPointException。

    根据 异常 NullPointException 提示,我定位了到 StringUtils.split 方法返回null,但不符合 逻辑啊, 不满足 分片,应该直接返回 本身 才对。况且以前也在用的,点进去查看了 jar,并看了方法。

    发现引用了 org.springframework.util.StringUtils

    /**
    	 * Split a {@code String} at the first occurrence of the delimiter.
    	 * Does not include the delimiter in the result.
    	 * @param toSplit the string to split
    	 * @param delimiter to split the string up with
    	 * @return a two element array with index 0 being before the delimiter, and
    	 * index 1 being after the delimiter (neither element includes the delimiter);
    	 * or {@code null} if the delimiter wasn't found in the given input {@code String}
    	 */
    	@Nullable
    	public static String[] split(@Nullable String toSplit, @Nullable String delimiter) {
    		if (!hasLength(toSplit) || !hasLength(delimiter)) {
    			return null;
    		}
    		int offset = toSplit.indexOf(delimiter);
    		if (offset < 0) {
    			return null;
    		}
    
    		String beforeDelimiter = toSplit.substring(0, offset);
    		String afterDelimiter = toSplit.substring(offset + delimiter.length());
    		return new String[] {beforeDelimiter, afterDelimiter};
    	}
    

    其实也解释的很清楚了,分隔符 不存在,就直接返回了 null

    分析

    在改成 org.apache.commons.lang3.StringUtils, 才是符合我们要求的

    public static String[] split(final String str, final String separatorChars) {
            return splitWorker(str, separatorChars, -1, false);
        }
        
    
    /**
         * Performs the logic for the {@code split} and
         * {@code splitPreserveAllTokens} methods that return a maximum array
         * length.
         *
         * @param str  the String to parse, may be {@code null}
         * @param separatorChars the separate character
         * @param max  the maximum number of elements to include in the
         *  array. A zero or negative value implies no limit.
         * @param preserveAllTokens if {@code true}, adjacent separators are
         * treated as empty token separators; if {@code false}, adjacent
         * separators are treated as one separator.
         * @return an array of parsed Strings, {@code null} if null String input
         */
        private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
            // Performance tuned for 2.0 (JDK1.4)
            // Direct code is quicker than StringTokenizer.
            // Also, StringTokenizer uses isSpace() not isWhitespace()
    
            if (str == null) {
                return null;
            }
            final int len = str.length();
            if (len == 0) {
                return ArrayUtils.EMPTY_STRING_ARRAY;
            }
            final List<String> list = new ArrayList<>();
            int sizePlus1 = 1;
            int i = 0, start = 0;
            boolean match = false;
            boolean lastMatch = false;
            if (separatorChars == null) {
                // Null separator means use whitespace
                while (i < len) {
                    if (Character.isWhitespace(str.charAt(i))) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i));
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            } else if (separatorChars.length() == 1) {//我的场景
                // Optimise 1 character case
                final char sep = separatorChars.charAt(0);
                while (i < len) {
                    if (str.charAt(i) == sep) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i)); //这里 分割
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            } else {
                // standard case    这里像是有满足 分隔符中的字符 就进行分割
                while (i < len) {
                    if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                        if (match || preserveAllTokens) {
                            lastMatch = true;
                            if (sizePlus1++ == max) {
                                i = len;
                                lastMatch = false;
                            }
                            list.add(str.substring(start, i));
                            match = false;
                        }
                        start = ++i;
                        continue;
                    }
                    lastMatch = false;
                    match = true;
                    i++;
                }
            }
            if (match || preserveAllTokens && lastMatch) {
                list.add(str.substring(start, i));  //遍历完自符,如果分隔符不存在,则返回 str 本身
            }
            return list.toArray(new String[list.size()]);
        }
    

    扩展

    org.apache.commons.lang3.StringUtils@split 方法能满足一般的情况,但是 他不会 对 空格做处理,如果有特殊要求,可以用 guava的 工具类

    List<String> hostNameList = Splitter.on(",")
                        .trimResults()
                        .omitEmptyStrings()//可以 选择是否对 空字符串 做处理
                        .splitToList(hostName);
    

    guava 是一种很强大的工具,能想到的功能基本都有,大家有空可以多看看它里面的实现和 设计思路。

  • 相关阅读:
    临时表各方式对比
    【译】表变量和临时表的比较(转)
    delete和truncate的一个严重区别
    各种临时表插入数据方式对比(包括自增列和GUID列)
    纤程模式的问题
    转 javascript针对DOM的应用(四)
    转 CSS兼容性(IE和Firefox)技巧大全 (四)
    转 CSS兼容性(IE和Firefox)技巧大全 (五)
    转 javascript针对DOM的应用(三)
    javascript基础知识大全(1)
  • 原文地址:https://www.cnblogs.com/idea-persistence/p/13639054.html
Copyright © 2020-2023  润新知