高性能的Split，针对char的分隔符号，比JDK String自带的高效.

copy from Commons Lange 3.5 StringUtils, 做如下优化:

1. 最后不做数组转换，直接返回List.

2. 可设定List初始大小.

3. preserveAllTokens 取默认值false

@param expectParts 预估分割后的List大小，初始化数据更精准

@return 如果为null返回null, 如果为""返回空数组
544882
public static List<String> split(@Nullable final String str, final char separatorChar, int expectParts) {
		if (str == null) {
			return null;
		}

		final int len = str.length();
		if (len == 0) {
			return ListUtil.emptyList();
		}

		final List<String> list = new ArrayList<String>(expectParts);
		int i = 0;
		int start = 0;
		boolean match = false;
		while (i < len) {
			if (str.charAt(i) == separatorChar) {
				if (match) {
					list.add(str.substring(start, i));
					match = false;
				}
				start = ++i;
				continue;
			}
			match = true;
			i++;
		}
		if (match) {
			list.add(str.substring(start, i));
		}
		return list;
	}
519803
public boolean partSubstringMatches(Part part, String s) {
        return part.length == s.length() && msg.regionMatches(part.index, s, 0, part.length);
    }
1328330
public static List<String> split(String s, String separator) {
        if (s == null) return null;
        List<String> rslt = null;
        if (s.isEmpty() || isEmpty(separator)) {
            rslt = new ArrayList<>(1);
            rslt.add(s);
            return rslt;
        }

        //return s.split(COMMA_SPLIT); - NOT WORKING when compiled to JS

        int start = 0;
        int p = start;
        do {
            int j = s.indexOf(separator, p);
            if (j == -1) {
                if (rslt == null) {
                    rslt = new ArrayList<>(1);
                    rslt.add(s);
                    return rslt;
                }
                rslt.add(s.substring(start));
                break;
            }
            if (j > 0 && s.charAt(j - 1) == '\\') {
                p = j + 1;
                continue;
            } else {
                if (rslt == null) rslt = new ArrayList<String>();
                rslt.add(s.substring(start, j));
                start = j + 1;
                p = start;
            }

        } while (true);

        return rslt;
    }
1229086
public static String join(String delimiter, String... parts) {
    if (parts == null) {
      return null;
    }

    if (delimiter == null) {
      delimiter = "";
    }

    StringBuilder stringBuilder = new StringBuilder();
    for (int i=0; i < parts.length; i++) {
      if (i > 0) {
        stringBuilder.append(delimiter);
      }
      stringBuilder.append(parts[i]);
    }
    return stringBuilder.toString();
  }
781374
public static List<String> split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase){
		if(StrUtil.isEmpty(str)){
			return new ArrayList<String>(0);
		}
		if(limit == 1){
			return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
		}
		
		if(StrUtil.isEmpty(separator)){//分隔符为空时按照空白符切分
			return split(str, limit);
		}else if(separator.length() == 1){//分隔符只有一个字符长度时按照单分隔符切分
			return split(str, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase);
		}
		
		final ArrayList<String> list = new ArrayList<>();
		int len = str.length();
		int separatorLen = separator.length();
		int start = 0;
		int i = 0;
		while(i < len){
			i = StrUtil.indexOf(str, separator, start, ignoreCase);
			if(i > -1){
				addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
				start = i + separatorLen;
				
				//检查是否超出范围（最大允许limit-1个，剩下一个留给末尾字符串）
				if(limit > 0 && list.size() > limit-2){
					break;
				}
			}else{
				break;
			}
		}
		return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
	}
652330
public static List<String> split(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty){
		return split(str, separator, limit, isTrim, ignoreEmpty, false);
	}
893979
public Part getPart(String partLabel)
  {
    if (m_parts!=null)
    {
        for (Object m_part : m_parts) {
            Part p = (Part) m_part;
            //if (p.getLabel().equals(partLabel)
            //	|| p.getLabel().equals(""+partLabel.charAt(0))) {
            if (p.getLabel().charAt(0) == partLabel.charAt(0)) {
                if (partLabel.length() > 1) {
                    p.setLabel(partLabel);
                }
                return p;
            }
        }
    	return null;
    }
    else
      return null;
  }
848614
@Override
    public InputStream getPart(String partName) throws IOException {
        ServletRESTRequestImpl ret = castRequest();
        if (ret != null)
            return ret.getPart(partName);
        return null;
    }
1259291
private static List<String> parseTokens(String exp) {
        if (exp == null) {
            return null;
        }
        List<String> tokens = new ArrayList<String>();
        Character commentChar = null;
        int start = 0;
        for (int i = 0; i < exp.length(); i++) {
            if (exp.charAt(i) == '"') {
                if (commentChar == null) {
                    commentChar = '"';
                } else if (commentChar == '"') {
                    commentChar = null;
                    tokens.add(exp.substring(start, i + 1).trim());
                    start = i + 1;
                }
            } else if (exp.charAt(i) == '\'') {
                if (commentChar == null) {
                    commentChar = '\'';
                } else if (commentChar == '\'') {
                    commentChar = null;
                    tokens.add(exp.substring(start, i + 1).trim());
                    start = i + 1;
                }
            } else if (exp.charAt(i) == '[') {
                if (commentChar == null) {
                    if (start != i) {
                        tokens.add(exp.substring(start, i).trim());
                    }
                    tokens.add("[");
                    start = i + 1;
                }
            } else if (exp.charAt(i) == ']') {
                if (commentChar == null) {
                    if (start != i) {
                        tokens.add(exp.substring(start, i).trim());
                    }
                    tokens.add("]");
                    start = i + 1;
                }
            } else if (exp.charAt(i) == '.') {
                if (commentChar == null) {
                    if (start != i) {
                        tokens.add(exp.substring(start, i).trim());
                    }
                    start = i + 1;
                }
            } else if (i == exp.length() - 1) {
                tokens.add(exp.substring(start, i + 1).trim());
            }
        }
        return tokens;
    }
341909
public static String displayParts(Object... parts) {
        StringBuilder builder = new StringBuilder("[");
        boolean first = true;
        for (Object part : parts) {
            if (first) {
                first = false;
            } else {
                builder.append('|');
            }
            if (part == null) {
                builder.append('?');
            } else {
                builder.append(display(part));
            }
        }
        builder.append(']');
        return builder.toString();
    }
981608
public static ArrayList<String> split(String source, String separator, boolean removeEmpty) {
        if (source == null || source.isEmpty())
            return null;

        ArrayList<String> values = new ArrayList<String>();
        if (separator == null || separator.isEmpty()) {
            values.add(source);
            return values;
        }

        String tmpStr = new String(source);

        int idx = 0;
        String tt;
        while (true) {
            int tmp = tmpStr.indexOf(separator, idx);
            if (tmp == -1) {
                tt = tmpStr.substring(idx);

                if (tt != null && !tt.isEmpty())
                    values.add(tmpStr.substring(idx));

                break;
            }

            tt = tmpStr.substring(idx, tmp);
            if (tt != null && !tt.isEmpty())
                values.add(tmpStr.substring(idx, tmp));

            idx = tmp + separator.length();
        }

        return values;
    }
1313021
private MatchingPart createEmoPermissionPart(String part, PartType partType) {
        part = unescapeSeparators(part);

        // Superclass guarantees part will be non-empty; no need to validate length prior to evaluating the first character
        switch (part.charAt(0)) {
            case 'c':
                // "createTable" only applies to table resource parts
                if (isTableResource(partType) && part.startsWith("createTable(") && part.endsWith(")")) {
                    String rison = part.substring(12, part.length() - 1);
                    return RisonHelper.fromORison(rison, CreateTablePart.class);
                }
                break;

            case 'i':
                if (part.startsWith("if(") && part.endsWith(")")) {
                    String condition = part.substring(3, part.length() - 1);
                    switch (partType) {
                        case SOR_TABLE:
                            return new SorTableConditionPart(Conditions.fromString(condition), _dataStore);
                        case BLOB_TABLE:
                            return new BlobTableConditionPart(Conditions.fromString(condition), _blobStore);
                        default:
                            return new ConditionPart(Conditions.fromString(condition));
                    }
                }
                break;

            case '*':
                if (part.length() == 1) {
                    // This is a pure wildcard
                    return getAnyPart();
                }
                break;

            case '?':
                if (part.length() == 1) {
                    // This is an "implied part exists" check
                    return EmoImpliedPartExistsPart.instance();
                }
                break;

        }

        // All other strings shortcuts for either a resource name or a wildcard expression for matching the name.
        Condition condition = Conditions.like(part);
        if (condition instanceof EqualCondition) {
            return new EmoConstantPart(part);
        }

        return new ConditionPart(condition);
    }
638749
public static String getAt(String list, String delimiter, int position, boolean ignoreEmpty, String defaultValue) {
	if (delimiter.length() == 1) return getAt(list, delimiter.charAt(0), position, ignoreEmpty, defaultValue);
	int len = list.length();

	if (len == 0) return defaultValue;
	int last = -1;
	int count = -1;

	char[] del = delimiter.toCharArray();
	char c;
	for (int i = 0; i < len; i++) {
	    c = list.charAt(i);
	    for (int y = 0; y < del.length; y++) {
		if (c == del[y]) {
		    if (ignoreEmpty && (last + 1) == i) {
			last = i;
			break;
		    }

		    count++;
		    if (count == position) {
			return list.substring(last + 1, i);
		    }
		    last = i;
		    break;
		}
	    }
	}

	if (position == count + 1) {
	    if (!ignoreEmpty || last + 1 < len) return list.substring(last + 1);
	}
	return defaultValue;
    }
813814
public static <T> List<T> popPart(Stack<T> surplusAlaDatas, int partSize) {
		if (isEmpty(surplusAlaDatas)) {
			return null;
		}

		final List<T> currentAlaDatas = new ArrayList<>();
		int size = surplusAlaDatas.size();
		// 切割
		if (size > partSize) {
			for (int i = 0; i < partSize; i++) {
				currentAlaDatas.add(surplusAlaDatas.pop());
			}
		} else {
			for (int i = 0; i < size; i++) {
				currentAlaDatas.add(surplusAlaDatas.pop());
			}
		}
		return currentAlaDatas;
	}
707940
private static String[] splitOnTokens(String text, String oneAnySymbolStr, String noneOrMoreSymbolsStr) {
		if (!hasWildcards(text, ONE_ANY_SYMBOL, NONE_OR_MORE_SYMBOLS)) {
			return new String[] { text };
		}
		List<String> list = new ArrayList<>();
		StringBuilder buffer = new StringBuilder();

		boolean escapeMode = false;
		final int length = text.length();
		for (int i = 0; i < length; i++) {
			final char current = text.charAt(i);
			switch (current) {
				case ESCAPE_CHAR:
					if (escapeMode) {
						buffer.append(current);
					} else {
						escapeMode = true;
					}
					break;
				case ONE_ANY_SYMBOL:
					if (escapeMode) {
						buffer.append(current);
						escapeMode = false;
					} else {
						flushBuffer(buffer, list);
						if (!list.isEmpty() && noneOrMoreSymbolsStr.equals(list.get(list.size() - 1))) {
							list.set(list.size() - 1, oneAnySymbolStr);
							list.add(noneOrMoreSymbolsStr);
						} else {
							list.add(oneAnySymbolStr);
						}
					}
					break;
				case NONE_OR_MORE_SYMBOLS:
					if (escapeMode) {
						buffer.append(current);
						escapeMode = false;
					} else {
						flushBuffer(buffer, list);
						if (list.isEmpty() || !noneOrMoreSymbolsStr.equals(list.get(list.size() - 1))) {
							list.add(noneOrMoreSymbolsStr);
						}
					}
					break;
				default:
					if (escapeMode) {
						buffer.append(ESCAPE_CHAR);
						escapeMode = false;
					}
					buffer.append(current);
			}
		}
		if (escapeMode) {
			buffer.append(ESCAPE_CHAR);
		}
		if (buffer.length() != 0) {
			list.add(buffer.toString());
		}

		return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
	}
1247864
private Pair<String, String> extractParam(String part) {
        int eqInx = part.indexOf('=');
        String paramName;
        String paramValue;
        if (eqInx < 0) {
            paramName = part;
            paramValue = null;
        } else {
            paramName = part.substring(0, eqInx);
            paramValue = part.substring(eqInx + 1);
        }
        return Pair.create(paramName, paramValue);
    }
39646
private static void checkPartForTextType(HashMap<String, Collection<String>> bodies, Part part) throws IOException, MessagingException {
        Object content = part.getContent();
        if (content instanceof CharSequence) {
            String ct = part.getContentType();
            Collection<String> value = bodies.get(ct);
            if (value != null) {
                value.add(content.toString());
            } else {
                value = new LinkedList<>();
                value.add(content.toString());
                bodies.put(ct, value);
            }
        } else if (content instanceof Multipart) {
            Multipart mp = (Multipart) content;
            for(int i = 0; i < mp.getCount(); i++) {
                checkPartForTextType(bodies, mp.getBodyPart(i));
            }
        }
    }