The Str class is an addition to, or replacement for, java.lang.String, with functionality inspired by Ruby. It is evolving quickly, and should be stable at the 4.0 release of ijdk.

In the following examples, the first section is an implementation with the JDK (as of Java 1.8 or so), and the second is the equivalent with ijdk.

of

The factory method of returns an instance of Str. This functionality is not yet optimized for memory usage by pooling strings, which may occur in a future release.

    String x = "abc";
    // x           : abc

    Str y = Str.of("abc");
    // y           : abc

join

A common way to create a string is by joining a collection with a separator.

    String[] ary = new String[] { "abc", "def", "ghi" };
    
    StringBuilder sb = new StringBuilder();
    boolean isFirst = true;
    for (String str : ary) {
        if (isFirst) {
            isFirst = false;
        }
        else {
            sb.append(", ");
        }
        sb.append(str);
    }
    String x = sb.toString();
    // x           : abc, def, ghi

    Str y = Str.join(ary, ", ");
    // y           : abc, def, ghi

repeat

To repeat a string:

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 3; ++i) {
        sb.append("ho");
    }
    String x = sb.toString();
    // x           : hohoho
    
    Str y = new Str("ho", 3);
    // y           : hohoho

And a character:

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 8; ++i) {
        sb.append('m');
    }
    String x = sb.toString();
    // x           : mmmmmmmm

    Str y = new Str('m', 8);
    // y           : mmmmmmmm

split

To split a string into a list, using a literal. (String.split(String arg) treats the argument as a regular expression.)

    int max = 3;

    Pattern pat = Pattern.compile(".", Pattern.LITERAL);
    String[] ary = pat.split("abc.def.ghi.jkl.mno", max);
    List<String> x = new ArrayList<>(Arrays.asList(ary));
    // x           : [abc, def, ghi.jkl.mno]

    List<String> y = new Str("abc.def.ghi.jkl.mno").split(".", max);
    // y           : [abc, def, ghi.jkl.mno]

toList

A common behavior is to convert a set of “words”, separated by commas and whitespace, into a list.

    String s = "first,    second  \nthird";
    
    List<String> x = new ArrayList<String>();
    StringTokenizer st = new StringTokenizer(s, " \t\n\r\f,");
    while (st.hasMoreTokens()) {
        String tk = st.nextToken();
        x.add(tk);
    }
    // x           : [first, second, third]

    List<String> y = new Str(s).toList();
    // y           : [first, second, third]

pad

The pad methods align a string to the left or right, adding extra characters to fit.

Padding to the right:

    StringBuilder sb = new StringBuilder("abc");
    for (int idx = 3; idx < 8; ++idx) {
        sb.append('*');
    }
    String x = sb.toString();
    // x           : abc*****

    String y = new Str("abc").pad('*', 8).toString();
    // y           : abc*****

Padding to the left:

    StringBuilder sb = new StringBuilder("abc");
    for (int idx = 3; idx < 8; ++idx) {
        sb.insert(0, '*');
    }
    String x = sb.toString();
    // x           : *****abc

    String y = new Str("abc").padLeft('*', 8).toString();
    // y           : *****abc

repeat

An existing Str can be repeated to create a new one.

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 3; ++i) {
        sb.append("ho");
    }
    String x = sb.toString();
    // x           : hohoho

    Str y = new Str("ho").repeat(3);
    // y           : hohoho

left, right

The left and right methods return a substring of the given number of characters, or a shorter string if of insufficient length.

    String s = "abcdef";
    int n = 4;
    String x = s.substring(0, n);
    // x           : abcd

    Str y = new Str(s).left(n);
    // y           : abcd
    String s = "abcdef";
    int n = 4;
    String x = s.substring(s.length() - n, s.length());
    // x           : cdef

    Str y = new Str(s).right(n);
    // y           : cdef

charAt

The method charAt handles negative values, returning a character from the end of the string. This method returns null when the index is not valid, not the (annoying) ArrayIndexOutOfBoundsException.

The method get is the same as charAt, matching the method name and behavior of Array.

    String s = "abcdef";
    int n = 2;
    Character x = n < s.length() ? s.charAt(n) : null;
    // x           : c

    Character y = new Str(s).charAt(n);
    // or y = new Str(s).get(n);
    // y           : c
    String s = "abcdef";
    int n = -3;
    Character x = Math.abs(n) < s.length() ? s.charAt(s.length() + n) : null;
    // x           : d

    Character y = new Str(s).charAt(n);
    // or y = new Str(s).get(n);
    // y           : d

substring, get

The substring method, with an equivalent get method taking two arguments, returns an Str, with the indices being positive or negative, as with charAt.

    String s = "abcdef";
    int i = 2;
    int j = 5;
    
    String x = s.substring(i, j + 1);
    // x           : cdef

    String y = new Str(s).substring(i, j);
    // or get(i, j)
    // y           : cdef
    String s = "abcdef";
    int i = 1;
    int j = -3;
    
    String x = s.substring(i, s.length() + j + 1);
    // x           : bcd

    String y = new Str(s).substring(i, j);
    // or get(i, j)
    // y           : bcd

substringBefore, substringAfter

These methods return an Str instance for the section of the string before, or after, a matching character.

    String s = "abcdef";

    int idx = s.indexOf('d');
    String x = s.substring(0, idx);
    // x           : abc

    String y = new Str(s).substringBefore('d');
    // y           : abc
    String s = "abcdef";

    int idx = s.indexOf('d');
    String x = s.substring(s.length() - idx + 1, s.length());
    // x           : ef

    String y = new Str(s).substringAfter('d');
    // y           : ef

startsWith

The startsWith methods take a character or a string, and handle issues with the string length being insufficent.

A character:

    String s = "abcdef";
    boolean x = s.length() >= 1 && s.charAt(0) == 'a';
    // x           : true
    
    boolean y = new Str(s).startsWith('a');
    // y           : true

A string:

    String s = "abcdef";
    boolean x = s.length() >= 3 && s.substring(0, 3).equals("abc");
    // x           : true
    
    boolean y = new Str(s).startsWith("abc");
    // y           : true

The methods also take the ignorecase option:

    String s = "AbCdEf";
    boolean x = s.length() >= 3 && s.substring(0, 3).equalsIgnoreCase("abc");
    // x           : true
    
    boolean y = new Str(s).startsWith("abc", EnumSet.of(Str.Option.IGNORE_CASE));
    // y           : true

endsWith

As with startsWith, the endsWith methods check the end of the string.

    String s = "abcdef";
    boolean x = s.length() >= 1 && s.charAt(s.length() - 1) == 'a';
    // x           : false
    
    boolean y = new Str(s).endsWith('a');
    // y           : false
    String s = "abcdef";
    boolean x = s.length() >= 3 && s.substring(3, s.length()).equals("def");
    // x           : true
    
    boolean y = new Str(s).endsWith("def");
    // y           : true

chomp

The chomp method removed one occurance of a new-line character, of Unix and DOS varieties.

    String s = "abcdef\n";
    String x = s.length() >= 1 && "\r\n".indexOf(s.charAt(s.length() - 1)) >= 0 ? s.substring(0, s.length() - 1) : s;
    // x           : abcdef
    
    Str y = new Str(s).chomp();
    // y           : abcdef

chompAll

This method removes all of the new-line characters from the end of the string.

    String s = "abcdef\n";
    String x = s;
    while (x.length() >= 1 && "\r\n".indexOf(x.charAt(x.length() - 1)) >= 0) {
        x = x.substring(0, x.length() - 1);
    }
    // x           : abcdef
    
    Str y = new Str(s).chompAll();
    // y           : abcdef

contains

To check whether an Str instance contains a substring. No, not much difference here.

    String s = "abcdef";
    boolean x = s.contains(String.valueOf('d'));
    // x           : true
    
    boolean y = new Str(s).contains('d');
    // y           : true

indexOf

Not a major difference here either, meaning that Str is consistent with String when possible.

    String s = "abcdef";
    int x = s.indexOf('d');
    // x           : 3
    
    int y = new Str(s).indexOf('d');
    // y           : 3

eq, eqi

These methods are the eq-uivalent (clever) of equals and equalsIgnoreCase.

    String s = "abcdef";
    String t = "abcdef";
    boolean x = s.equals(t);
    // x           : true
    
    boolean y = new Str(s).eq(t);
    // y           : true
    String s = "abcdef";
    String t = "abcdef";
    boolean x = s.equalsIgnoreCase(t);
    // x           : true
    
    boolean y = new Str(s).eq(t);
    // y           : true

snip

To cut a string down to a length, then append a hyphen:

    String s = "abcdef";
    String x = s.substring(0, 3) + '-';
    // x           : abc-
    
    Str y = new Str(s).snip(4);
    // y           : abc-

isEmpty

To the isEmpty method, Str adds handling of ignoring whitespace.

    String s = "abcdef";
    boolean x = s.trim().isEmpty();
    // x           : false
    
    boolean y = new Str(s).isEmpty(Str.Option.IGNORE_WHITESPACE);
    // y           : false

quote

To add quotation marks:

    String s = "abcdef";
    String x = '"' + s + '"';
    // x           : "abcdef"
    
    Str y = new Str(s).quote();
    // y           : "abcdef"

unquote

And to remove quotation marks, if they exist:

    String s = "\"abcdef\"";
    String x = s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"' ? s.substring(1, s.length() - 1) : s;
    // x           : abcdef
    
    Str y = new Str(s).unquote();
    // y           : abcdef

compareTo, alphanumeric

In comparison to the String#compareTo method, Str adds support for comparing alphanumerically, such that xyz3 precedes xyz12

    String s = "abc12";
    String t = "abc3";
    int x = Integer.valueOf(s.substring(3, s.length())).compareTo(Integer.valueOf(t.substring(3, t.length())));
    // x           : 1
    
    int y = new Str(s).compareTo(new Str(t), EnumSet.of(Str.Option.ALPHANUMERIC));
    // y           : 1

compareTo, ignorecase

And Str can also ignore case for comparisons:

    String s = "Abc";
    String t = "def";
    int x = s.toUpperCase().compareTo(t.toUpperCase());
    // x           : -3
    
    int y = new Str(s).compareTo(new Str(t), EnumSet.of(Str.Option.IGNORE_CASE));
    // y           : -3

lt, lte, gt, gte

Because x > y is unequivocally (clever, again) clearer than x.compareTo(y) > 0, Str offers the same methods as Obj, for less than, less than or equal to, greater than, and greater than or equal to.

    String s = "Abc";
    String t = "def";
    boolean x = s.compareTo(t) < 0;
    // x           : true
    
    boolean y = new Str(s).lt(new Str(t));
    // y           : true
    String s = "Abc";
    String t = "def";
    boolean x = s.compareTo(t) <= 0;
    // x           : true
    
    boolean y = new Str(s).lte(new Str(t));
    // y           : true
    String s = "Abc";
    String t = "def";
    boolean x = s.compareTo(t) > 0;
    // x           : false
    
    boolean y = new Str(s).gt(new Str(t));
    // y           : false
    String s = "Abc";
    String t = "def";
    boolean x = s.compareTo(t) >= 0;
    // x           : false
    
    boolean y = new Str(s).gte(new Str(t));
    // y           : false

firstChar

As with Array, Str has the first and last methods, returning a character or string of a given length.

    String s = "abcdef";
    char x = s.charAt(0);
    // x           : a
    
    char y = new Str(s).first();
    // y           : a
    String s = "abcdef";
    String x = s.substring(0, 3);
    // x           : abc
    
    Str y = new Str(s).first(3);
    // y           : abc
    String s = "abcdef";
    char x = s.charAt(s.length() - 1);
    // x           : f
    
    char y = new Str(s).last();
    // y           : f
    String s = "abcdef";
    String x = s.substring(s.length() - 3, s.length());
    // x           : def
    
    Str y = new Str(s).last(3);
    // y           : def

replaceAll

In comparison to String#replaceAll, which operates on a regular expression, Str#replaceAll handles the argument as a literal.

    String s = "ab.ac.ad";
    String t = ".a";
    String u = ".x";
    String x = "";
    int prev = 0;
    int idx = s.indexOf(t, prev);
    while (idx >= 0) {
        x += s.substring(prev, idx);
        x += u;
        prev = idx + t.length();
        idx = s.indexOf(t, prev);
    }
    x += s.substring(prev, s.length());
    // x           : ab.xc.xd
    
    Str y = new Str(s).replaceAll(t, u);
    // y           : ab.xc.xd

replaceAll, ignore case

And for more excitement, an option can ignore case for the replacements. Voila:

    String s = "ab.Ac.ad";
    String t = ".a";
    String u = ".x";
    String x = "";
    String su = s.toUpperCase();
    String tu = t.toUpperCase();
    int prev = 0;
    int idx = su.indexOf(tu, prev);
    while (idx >= 0) {
        x += s.substring(prev, idx);
        x += u;
        prev = idx + t.length();
        idx = su.indexOf(tu, prev);
    }
    x += s.substring(prev, s.length());
    // x           : ab.xc.xd
    
    Str y = new Str(s).replaceAll(t, u, EnumSet.of(Str.Option.IGNORE_CASE));
    // y           : ab.xc.xd

trimLeft, trimRight

Finally, these methods remove whitespace at the left or right of the string.

    String s = "   abc def   ";
    int idx = 0;
    while (idx < s.length() && Character.isWhitespace(s.charAt(idx))) {
        ++idx;
    }
    String x = idx < s.length() ? s.substring(idx, s.length()) : "";
    // x           : "abc def   "
    
    Str y = new Str(s).trimLeft();
    // y           : "abc def   "
    String s = "   abc def   ";
    int idx = s.length() - 1;
    while (idx >= 0 && Character.isWhitespace(s.charAt(idx))) {
        --idx;
    }
    String x = idx >= 0 ? s.substring(0, idx + 1) : "";
    // x           : "   abc def"
    
    Str y = new Str(s).trimRight();
    // y           : "   abc def"

Plans

For consistency, Str will most likely be refined to support the Array functionality, as an Array<Character>.

Related