Groovy- and Ruby-like extensions to the JDK.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs Update javadoc Jan 31, 2018
src Add Str#trim, trimRight, trimLeft Aug 13, 2018
.gitignore
LICENSE.txt Add MIT license. Jan 18, 2018
README.md Integrate HasInstanceValues into core classes. Jun 9, 2018
aliases.zsh Add Map class, extending TreeMap Apr 13, 2017
build.gradle Add Iterate#eachWithIndex. Move collection and iterators to ijdk.coll… Jul 4, 2018
deploy.gradle Fix package name -- ijdk, not diffj. Jul 16, 2017
runtest.rb Add Map class, extending TreeMap Apr 13, 2017
settings.gradle Merge ijdkproj into ijdk Mar 12, 2015

README.md

Overview

IJDK (Incava Java Development Kit) is a library of general-purpose Java code, much of it inspired by and modeled on the equivalent in Ruby. For example, reading a file:

    List<String> lines = IO.readLines("foo.txt");

Note the similarity to idiomatic Ruby:

    lines = IO.readlines "foo.txt"

Usage

IJDK has a very expansive (and growing) library for Java I/O and collections, as an alternative to the JDK. IJDK is mostly inspired by Ruby, and parallels Ruby classes (such as Map/Hash and List/Array) closely, usually moreso than it does the Apache Commons and Guava libraries.

As IJDK grows and gets more behavior, modules -- such as I/O -- may be split into subprojects.

Enhanced collections

IJDK contains collection classes that extend the List (ArrayList) and Map (HashMap/TreeMap) classes from the JDK.

Array

An extension of ArrayList, with Ruby-like methods (thus the name matching Array in Ruby).

    Integer x = null;
    Array<Integer> nums = Array.of(1, 3, 5, 7);
    
    x = nums.get(0);
    assertEqual(1, x);
    
    x = nums.get(-1);
    assertEqual(7, x);
    
    nums.append(9).append(11).append(13);
    assertEqual(Array.of(1, 3, 5, 7, 9, 11, 13), nums);

    x = nums.get(-3);
    assertEqual(9, x);
    
    x = nums.first();
    assertEqual(1, x);

    x = nums.last();
    assertEqual(13, x);
    
    x = nums.takeFirst();
    assertEqual(1, x);
    assertEqual(Array.of(3, 5, 7, 9, 11, 13), nums);
    
    x = nums.takeFirst();
    assertEqual(3, x);
    assertEqual(Array.of(5, 7, 9, 11, 13), nums);

    x = nums.takeLast();
    assertEqual(13, x);
    assertEqual(Array.of(5, 7, 9, 11), nums);

    StringArray sa = nums.toStringArray();
    assertEqual(StringArray.of("5", "7", "9", "11"), sa);

    nums.append(2).append(2).append(2);
    assertEqual(Array.of(5, 7, 9, 11, 2, 2, 2), nums);

    Array<Integer> uniq = nums.unique();
    assertEqual(Array.of(5, 7, 9, 11, 2), uniq);

    assertEqual(true, nums.containsAny(2, 3));
    assertEqual(false, nums.containsAny(3, 4));

    nums.removeAll(2);
    assertEqual(Array.of(5, 7, 9, 11), nums);

    nums.set(0, 4);
    assertEqual(Array.of(4, 7, 9, 11), nums);

    nums.set(-1, 10);
    assertEqual(Array.of(4, 7, 9, 10), nums);

    nums.set(-2, 8);
    assertEqual(Array.of(4, 7, 8, 10), nums);

    nums.set(1, 6);
    assertEqual(Array.of(4, 6, 8, 10), nums);

    x = nums.getRandomElement();
    assertEqual(true, Array.of(4, 6, 8, 10).contains(x));

    String str = nums.join(" + ");
    assertEqual("4 + 6 + 8 + 10", str);

    Array<Integer> odds = Array.of(1, 3, 5);
    Array<Integer> evens = Array.of(2, 4, 6);
    Array<Integer> numbers = odds.plus(evens);
    assertEqual(Array.of(1, 3, 5, 2, 4, 6), numbers);
    
    Array<Integer> squares = numbers.minus(Array.of(2, 3, 5, 6));
    assertEqual(Array.of(1, 4), squares);

    Array<Integer> elements = numbers.elements(1, 0, -2, 0, -4);
    assertEqual(Array.of(3, 1, 4, 1, 5), elements);

    Array<Integer> ary = Array.of(3, 7, 1).sorted();
    assertEqual(Array.of(1, 3, 7), ary);

    Array<Integer> ary = Array.of(3, 7, 1).reverse();
    assertEqual(Array.of(1, 7, 3), ary);

Hash

An extension of HashMap, with Ruby-like methods (thus the name matching Hash in Ruby, and avoiding a collision with java.util.Map).

    // generic type inferred by context:
    Hash<String, String> h = Hash.empty();
    assertEqual(new HashMap<String, String>(), h);

    // creates a map from one key/value pair
    Hash<String, String> h = Hash.of("one", "1");
    HashMap<String, String> expected = new HashMap<String, String>();
    expected.put("one", "1");    
    assertEqual(expected, h);

    // creates a map from two key/value pairs
    Hash<String, String> h = Hash.of("one", "1", "two", "2");
    HashMap<String, String> expected = new HashMap<String, String>();
    expected.put("one", "1");    
    expected.put("two", "2");
    assertEqual(expected, h);

    // creates a map from three key/value pairs
    Hash<String, String> h = Hash.of("first", "abc", "second", "def", "third", "ghi");
    HashMap<String, String> expected = new HashMap<String, String>();
    expected.put("first", "abc");
    expected.put("second", "def");
    expected.put("third", "ghi");
    assertEqual(expected, h);

    // set() returns the map, so methods can be chained:
    Hash<String, String> h = Hash.of("first", "abc");
    h.set("second", "def").set("third", "ghi").set("fourth", "jkl");
    
    HashMap<String, String> expected = new HashMap<String, String>();
    expected.put("first", "abc");
    expected.put("second", "def");
    expected.put("third", "ghi");
    expected.put("fourth", "jkl");
    
    assertEqual(expected, h);

    // same as keySet, but a shorter name:
    Hash<String, String> h = Hash.of("first", "abc", "second", "def", "third", "ghi");
    
    Set<String> expected = new HashSet<String>();
    expected.add("first");
    expected.add("second");
    expected.add("third");
    
    assertEqual(expected, h.keys(), message("h.keys", h.keys()));

    // same as entrySet, but, like keys/keySet, a shorter name:
    Hash<String, String> h = Hash.of("first", "abc", "second", "def", "third", "ghi");

    HashMap<String, String> expected = new HashMap<String, String>();
    expected.put("first", "abc");
    expected.put("second", "def");
    expected.put("third", "ghi");
    
    assertEqual(expected.entrySet(), h.entries(), message("h.entries", h.entries()));

    // Hash is iterable, and can be used in for loops:
    Hash<String, String> h = Hash.of("first", "abc", "second", "def", "third", "ghi");
    for (java.util.Map.Entry<String, String> entry : h) {
        String exp = h.get(entry.getKey());
        assertEqual(exp, entry.getValue(), message("entry", entry));
    }

    Hash<String, String> h = Hash.of("first", "abc", "second", "def", "third", "ghi");
    assertEqual("abc", h.fetch("first"));
    assertEqual("xyz", h.fetch("fourth", "xyz"));
    assertEqual(null, h.fetch("fourth", null));
    try {
        h.fetch("fourth");
    }
    catch (IllegalArgumentException iae) {
        // expected
    }

Common Collections

Classes for common Java collections of generics, such as a list of strings, and a list of integers:

StringArray

Formerly StringList, an Array of Strings.

   // instead of Array<String>; varargs constructor
   StringArray sa = new StringArray("apple", "banana", "cherry");
   boolean result = sa.anyStartsWith("ba");  // true
   boolean result = sa.anyStartsWith("do");  // false
   StringArray lines = sa.toLines();          // returns each element appended with "\n"

IntegerArray

   // instead of Array<Integer>; varargs constructor
   IntegerArray il = new IntegerArray(3, 6, 9);
   int max = il.maximum();                 // max == 9
   int avg = il.average();                 // avg == 6
   int min = il.minimum();                 // min == 3

Tuples

    Pair<String, Integer> score = Pair.create("Bird", 33);
    // score.first() == "Bird", score.second() == 33

Similarly, the nearly identical KeyValue:

    KeyValue<String, Double> kv = KeyValue.of("one", 1.23);
    // kv.key() == "one", kv.value() == 1.23

And Triple:

    Triple<String, Integer, Double> t = Triple.of("abc", 123, 3.14);

Str

Str is an alternative to java.lang.String, more inspired by the Ruby String class.

    Str s = Str.of("hello, world");
    assertThat(s, equalTo("hello, world"));

    Character ch = s.get(5);
    assertThat(ch, equalTo(','));

    ch = s.get(-5);
    assertThat(ch, equalTo('w'));

    ch = s.first();
    assertThat(ch, equalTo('h'));

    ch = s.last();
    assertThat(ch, equalTo('d'));

    Boolean b = s.startsWith("hello");
    assertThat(b, equalTo(true));

    b = s.endsWith("rld");
    assertThat(b, equalTo(true));

    Str t = s.left(2);
    assertThat(t, equalTo("he"));

    t = s.right(5);
    assertThat(t, equalTo("world"));

    Str u = t.padLeft('.', 10);
    assertThat(u, equalTo(".....world"));

    u = t.pad('*', 8);
    assertThat(u, equalTo("world***"));

    u = new Str("ho! ", 3);
    assertThat(u, equalTo("ho! ho! ho! "));

    List<String> list = Str.of("first,    second  \nthird").toList();
    assertThat(list, equalTo(org.incava.ijdk.collect.Array.of("first", "second", "third")));

    t = s.quote();
    assertThat(t, equalTo("\"hello, world\""));

    u = t.unquote();
    assertThat(u, equalTo("hello, world"));

    t = s.snip(6);
    assertThat(t, equalTo("hello-"));

Range

A Range is a pair of integers. It converts to arrays, and supports iteration.

    Range r = new Range(3, 7);
    r.includes(4); // true
    r.includes(2); // false

    // inclusive of both first and last values
    for (Integer i : r) {
        // iterate from 3 *through* 7
    }

    Array<Integer> list = r.toArray(); // list == [ 3, 4, 5, 6, 7 ]

    // exclusive of last value
    for (Integer i : r.upTo()) {
        // iterate from 3 *through* 6
    }

Iterators

Null-safe Iterators

"Safe" iterators for arrays and collections, which handles the case when they are null.

    String[] ary = new String[0];
    for (String str : Iterate.over(ary)) { // executes zero times
    }
    String[] ary = null;
    for (String str : Iterate.over(ary)) {  // also executes zero times
    }
    List<String> list = null;
    for (String str : Iterate.over(list)) {  // executes zero times
    }

Thus the common idiom as shown below is simplified:

    List<String> list;   // set somewhere
    if (list != null) {
        for (String str : list) {
        }
    }

now:

    List<String> list;   // set somewhere
    for (String str : Iterate.over(list)) {
    }

Numeric Iterators

Execute a given number of times, similar to Ruby:

    3.times { puts "hello" }
    for (Integer i : Iterate.count(3)) {
        ICore.puts("hi");
    }

Index/Value Iterators

Returned by Iterate.each (as opposed to Iterate.over, the iterator class It has a value and an index (which starts from zero):

    List<String> list = Arrays.asList(new String[] { "a", "b", "c" });
    for (It<String> it : Iterate.each(list)) {
        // use it.index() and it.value()
    }
    String[] ary = new String[] { "a", "b", "c" };
    for (It<String> it : Iterate.each(ary)) {
        // use it.index() and it.value()
    }

Non-Null Iterator

Iterate.overNonNull yields only non-null elements.

    List<String> list = Arrays.asList(null, "b", null, null, "e", null, "g");
    for (It<String> it : Iterate.overNonNull(list)) {
        // will get the strings "b", "e", and "g" only
    }

MultiMap

Does one-to-many mappings.

    MultiMap<String, String> firstToLastNames = new MultiMap<String, String>();
    firstToLastNames.put("James", "Gosling");
    firstToLastNames.put("James", "Rumbaugh");
    firstToLastNames.put("James", "Foley");
    for (String lastName : firstToLastNames.get("James")) {
    }

Int

An alternate class to Integer:

    Integer num = Int.toInteger("1");   // num == 1
    Integer num = Int.toInteger("");    // num == null
    Integer num = Int.toInteger("xyz"); // num == null
    Integer num = Int.toInteger(null);  // num == null
    Int num = Int.of("1");   // num.obj() == 1
    Int num = Int.of("");    // num.obj() == null
    Int num = Int.of("xyz"); // num.obj() == null
    Int num = Int.of(null);  // num.obj() == null

Pathname

Pathname extends java.io.File with Ruby-like functionality:

    Pathname pn = new Pathname("abc/def.txt");
    pn.baseName();     // == "def.txt"
    pn.relativePath(); // == "abc/def.txt"
    pn.extension();    // == "txt"

Comp

org.incava.ijdk.lang.Comp extends and replaces Comparable (compareTo) usage, normalizing the result to be simply -1, 0, or 1. Comp also contains the methods lt (less than), lte (less than or equal), gt (greater than), and gte (greater than or equal), for simpler comparisons:

   if (Comp.gte("abc", "bbc")) {
   }
   if (Comp.lt("def", "fed")) {
   }

Assertions

org.incava.test.Assertions has been split apart from IJDK, and is available at Attest.

Miscellaneous

  • colorized strings (on ANSI terminals)

  • logical package structure

The primary classes are in org.incava.ijdk.lang (roughly the equivalent of java.lang) and org.incava.ijdk.collect.

The names of the shadow classes follow the convention of the class that they wrap, with shorter names, such as Str (String), Int (Integer), Obj (Object), etc.

Installation

From the GitHub project ijdk, download and build the project with Gradle.

IJDK is available in the Maven repository.

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.incava', name: 'ijdk', version: '3.3.2'
}

Help

Email me at jeugenepace at gmail dot com if you have questions or feedback about IJDK.

More Examples

More detailed examples are at the IJDK Cookbook.

Credits

Much of this was inspired by Ruby and Perl.