Skip to content

Instantly share code, notes, and snippets.

@lululau
Created April 1, 2021 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lululau/c740a2a3915358ec5ed0c8d613687f19 to your computer and use it in GitHub Desktop.
Save lululau/c740a2a3915358ec5ed0c8d613687f19 to your computer and use it in GitHub Desktop.
ruby-vs-java-python-rust

Range and Number

Collections

Enumerable

all?

Java

Stream.of(1, 2, 3, 4, 5).allMatch(x -> x < 10);

Python

all(x < 10 for x in [1, 2, 3, 4, 5])

Rust

let x = vec![1, 2, 3, 4];
let y = x.iter().all(|e| *e < 10);

any?

Stream.of(1, 2, 3, 4, 5).anyMatch(x -> x < 10);

Python

any(x < 10 for x in [1, 2, 3, 4, 5])

Rust

let x = vec![1, 2, 3, 4];
let y = x.iter().any(|e| *e < 10);

collect / map

Java

Stream.of(1, 2, 3, 4).map(x -> x * 2);

Python

[e * 2 for e in [1, 2, 3, 4]]

Rust

let x = vec![1, 2, 3, 4];
let y: Vec<i32> = x.iter().map(|x| x * 2).collect();

flat_map

Java

List a = Arrays.asList(1, 2, 3, 4);
List b = Arrays.asList(3, 4, 5, 6);
List result = Stream.of(a, b).flatMap(e -> e.stream()).collect(Collectors.toList());

Python

x = [[1, 2, 3], [4, 5, 6]]

[e for i in x for e in i]

Rust

let x = vec![
    vec![1, 2, 3],
    vec![4, 5, 6]
];
let y: Vec<&i32> = x.iter().flat_map(|e| e ).collect();

count

Java

IntStream.range(1, 11).filter(e -> e % 2 == 0).count()

Python

sum(1 for e in [1, 2, 3, 4] if e < 3)

Rust

let x = vec![1, 2, 3, 4];
let y = x.iter().filter(|e| **e < 3).count();

find

Java

Integer i = Stream.of(1, 2, 3, 4).filter(x -> x > 2).findFirst();

Python

next((e for e in [1, 2, 3, 4, 5, 6] if e > 3), None)

Rust

let x = vec![1, 2, 3, 4];
let y = x.iter().find(|e| **e > 3);

each_with_index

Java

// Java 没有对应的现成方法
// Method A:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
    .filter(i -> names[i].length() <= i)
    .mapToObj(i -> names[i])
    .collect(Collectors.toList());

// Method B:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
    .filter(n -> n.length() <= index.incrementAndGet())
    .collect(Collectors.toList());

// Method C:
// Guava since version 21
Streams.mapWithIndex(Stream.of("a", "b", "c"),
                     (str, index) -> str + ":" + index) // will return Stream.of("a:0", "b:1", "c:2")

Python

for i, v in enumerate(a):
   print "{} : {}".format(i, v)

Rust

let x = vec![1, 2, 3, 4];
x.iter().enumerate().for_each(|(idx, e)| {
    println!("Index: {}, Value: {}", idx, e);
});

each_with_object

Java

h = {a: 1, b: 2, c: 3}
result = h.each_with_object({}) { |(k, v), hash| hash[k] = v }
Map<String, Integer> h = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> result = h.entrySets().stream().reduce(new HashMap<String, Integer>(), (hash, es) -> {
        hash.put(es.getKey(), es.getValue() * 2);
        return hash;
    }, (h1, h2) -> {
        h1.putAll(h2);
        return h1;
    });

Python

def create_url_table(urls):
  return {url: get_title(url) for url in urls if 'google' not in url}

Rust

entries

Java

Map#entrySet()

Python

d.items()  # Or: list(d.items())

Rust

let x: HashMap<String, i32> = HashMap::new();
let entries: Vec<_> = x.iter().collect_vec();

find_all / select

Java

Stream#filter()

Python

[e for e in [1, 2, 3, 4] if e > 2]

Rust

let x = vec![1, 2, 3, 4];
let x = x.iter().filter(|e| **e > 2).collect_vec();

group_by

Java

Map<Boolean, List<Integer>> map = IntStream.range(1, 11).boxed().collect(groupingBy(x -> x % 2 == 0));

Python

Rust

use itertools::Itertools;

// group data into runs of larger than zero or not.
let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
// groups:     |---->|------>|--------->|

for (key, group) in data.into_iter().group_by(|elt| *elt >= 0) {
    // Check that the sum of each group is +/- 4.
    assert_eq!(4, group.iter().fold(0_i32, |a, b| a + b).abs());
}

inject / reduce

Java

IntStream.range(1, 11).boxed().reduce(Integer::sum);

Python

reduce(function, iterable[, initializer])

Rust

let x = vec![1, 2, 3, 4];
let s1 = x.iter().fold(0, |s, e| s + *e);

map

Java

Stream map = IntStream.range(1, 11).boxed().map(x -> x * 2);

Python

[e * 2 for e in [1, 2, 3, 4, 5]]

Rust

let x = vec![1, 2, 3, 4];
let s = x.iter().map(|x| *x + 2).collect_vec();

max / max_by

Java

IntStream.range(1, 11).max();
IntStream.range(1, 11).boxed().max(Comparators.comparable());
IntStream.range(1, 11).boxed().max(Comparator.comparing(x -> -x));
IntStream.range(1, 11).boxed().collect(maxBy(Comparator.comparing(x -> x)));

Python

max(e for e in [1, 2, 3, 4])
max((e for e in [1, 2, 3, 4]), key=lambda e: -e)

Rust

let x = vec![1, 2, 3, 4];
let m = x.iter().max();
let m2 = x.iter().max_by_key(|e| -(**e) );
let m3 = x.iter().max_by(|a, b| (*b).cmp(*a));

partition

Java

IntStream.range(1, 11).boxed().collect(partitioningBy(x -> x > 5))

Python

Rust

let x = vec![1, 2, 3, 4];
let (x, y): (Vec<i32>, Vec<i32>) = x.iter().partition(|e| **e > 2);

reverse_each

Java

IntStream.range(1, 11).boxed().sorted(Collections.reverseOrder()).forEach(System.out::println);

Python

for i in reversed([1, 2, 3, 4]):
    print(i)

Rust

let x = vec![1, 2, 3, 4];
x.iter().rev().for_each(|e| println!("{}", e));

sort

Java

List l = Arrays.asList(4, 3, 2, 1);
l.sort(Comparator.naturalOrder());
Collections.sort(l);
System.out.println(l);
l.stream().sorted();
l.stream().sorted(Comparators.comparable());
l.stream().sorted(Comparator.naturalOrder());

Python

sorted([3, 1, 4, 2])

Rust

let x = vec![2, 3, 1, 4];
let x = x.iter().sorted().collect_vec();

sort_by

Java

people.stream().sorted(Comparator.comparing(x -> x.getAge()))

Python

from operator import itemgetter
h = [{"name": "Jack", "age": 20}, {"name": "David", "age": 30}, {"name": "Lucy", "age": 10}]
sorted(h, key=itemgetter("age"))

Rust

let x = vec![
    Person { name: String::from("Jack"), age: 20 },
    Person { name: String::from("David"), age: 10 },
    Person { name: String::from("Lucy"), age: 30 },
];

let x = x.iter().sorted_by_key(|p| &p.name).collect_vec();

sum

Java

IntStream.range(1, 11).sum();
Stream.of(1, 2, 3, 4).mapToInt(Integer::intValue).summaryStatistics().getSum();

Python

sum([1, 2, 3, 4])

Rust

let y = vec![2, 3, 4, 5];
let y = y.iter().sum();

uniq

Java

Stream.of(1, 2, 3, 1, 2, 3).distinct().forEach(System.out::println);

Python

list(set([1, 2, 3, 4, 1, 2]))

Rust

let y = vec![2, 3, 4, 5];
let y = y.iter().unique().collect_vec();

zip

Java

Guava:

Streams.zip(
            Stream.of("foo1", "foo2", "foo3"),
            Stream.of("bar1", "bar2"),
            (arg1, arg2) -> arg1 + ":" + arg2);

Python

x = [1, 2, 3]
y = [4, 5, 6]
zipped = zip(x, y)

Rust

let a1 = [1, 2, 3];
let a2 = [4, 5, 6];
let mut iter = a1.iter().zip(a2.iter());

Array

&

Java

commons-collections ListUtils :

public static List intersection(List list1, List list2)

Python

a = [1,2,3,4,5]
b = [1,3,5,6]
list(set(a) & set(b))

[x for x in a if x in b]

Rust

use std::collections::HashSet;

let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();

let mut intersection = a.intersection(&b);

|

Java

commons-collection ListUtils#union()

Returns a new list containing the second list appended to the first list.

Result may be not unique.

Python

a = ['Orange and Banana', 'Orange Banana']
b = ['Grapes', 'Orange Banana']
c = ['Foobanana', 'Orange and Banana']
list(set().union(a,b,c))

Rust

use std::collections::HashSet;

let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();

let mut union_iter = a.union(&b);

+

Java

Stream.concat(stream1, stream2);
List#addAll();
ListUtils.union(l1, l2);

Python

[1, 2] + [3, 4]

Rust

let y = vec![2, 3, 4, 5];
let y = y.iter().chain(y.iter()).collect_vec();

-

Java

boolean result = List#removeAll();
List result = ListUtils.removeAll(col, removeCol); // commons-collections

Python

set([1,2,3,4]) - set([2,5])

Rust

use std::collections::HashSet;

let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();

let mut difference = a.difference(&b);

<<

Java

List#add()

Python

x = [1, 2, 3]
x.append(4)

Rust

let mut y = vec![2, 3, 4, 5];
y.push(10);

compact

Java

boolean result = List#removeAll(Collections.singleton(null));
List result = ListUtils.removeAll(col, Collections.singleton(null)); // commons-collections

Python

[e for e in x if e != None]

Rust

delete

Java

see compact

Python

x = [3, 4, 5, 6]
x.remove(3)

Rust

let mut y = vec![2, 3, 4, 5];
y.remove(1);  // remove by index

flatten

Java

List<List<Object>> list = ...
    List<Object> flat = list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

Python

flat_list = [item for sublist in t for item in sublist]

Rust

let data = vec![vec![1, 2, 3, 4], vec![5, 6]];
let flattened = data.into_iter().flatten().collect::<Vec<u8>>();
assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]);

include?

Java

Collection#contains();
Stream#anyMatch();

Python

2 in [2, 3, 4, 5]

Rust

let mut y = vec![2, 3, 4, 5];
y.contains(&2);

join

Java

String#join();
String result = artists.stream().map(Artist::getName).collect(joining(",", "[", "]"));

Python

"XX".join([str(e) for e in x])

Rust

let mut y = vec![2, 3, 4, 5];
let y: String = y.iter().map(|e| (*e).to_string() ).join(",");

last

Java

// Guava
lastElement = Iterables.getLast(iterableList);
// You can also provide a default value if the list is empty, instead of an exception:
lastElement = Iterables.getLast(iterableList, null);

stream.skip(count - 1).findFirst().get();

Python

x[-1]

Rust

let mut y = vec![2, 3, 4, 5];
println!("{:?}", y.last().unwrap());

first

Java

String firstElement = Iterables.getFirst(strings, null); // Guava

Stream#findfirst();

Python

x[0]

Rust

let mut y = vec![2, 3, 4, 5];
println!("{:?}", y.first().unwrap());

push / pop / shift / unshift

Java

// push
List#add();
Stack#push();
LinkedList#addLast();

// pop
list.remove(list.size-1);
Stack#pop();
LinkedList#pollLast();

// shift
list.remove(0);
LinkedList#pollFirst();

// unshift
list.add(0, obj);
LinkedList#addFirst();

Python

use deque

Rust

use VecDeque

reverse

Java

Collections.reverse();
ArrayUtils.reverse(); // commons-lang
IntStream.range(1, 11).boxed().sorted(Collections.reverseOrder()).forEach(System.out::println);

Python

list(reversed([1, 2, 3, 4]))

Rust

let mut y = vec![2, 3, 4, 5];
y.reverse();
let y = y.iter().rev().collect_vec();

rotate

Java

Collections.rotate();

Python

Rust

let mut y = vec![2, 3, 4, 5];
y.reverse();

shuffle

Java

Collections.shuffle();

Python

random.shuffle(x)

Rust

use rand::thread_rng;
use rand::seq::SliceRandom;

fn main() {
    let mut vec: Vec<u32> = (0..10).collect();
    vec.shuffle(&mut thread_rng());
    println!("{:?}", vec);
}

sample

Java

Python

random.sample([1, 2, 3, 4, 5], 3)

Hash

each_key / each_value

Java

Map#keySet();
Map#values()

Python

h = {'name': 'Jack', 'age': 1}
h.keys()
h.values()

Rust

use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);

for key in map.keys() {
    println!("{}", key);
}

invert

Java

MapUtils.invertMap(); // commons-collections

Python

inv_map = {v: k for k, v in my_map.items()}

Rust

merge

Java

Map#pullAll()

Python

z = x | y  # 3.9+
z = {**x, **y}  # 3.5+

Rust

use std::collections::HashMap;

// Mutating one map
fn merge1(map1: &mut HashMap<(), ()>, map2: HashMap<(), ()>) {
    map1.extend(map2);
}

// Without mutation
fn merge2(map1: HashMap<(), ()>, map2: HashMap<(), ()>) -> HashMap<(), ()> {
    map1.into_iter().chain(map2).collect()
}

// If you only have a reference to the map to be merged in
fn merge_from_ref(map: &mut HashMap<(), ()>, map_ref: &HashMap<(), ()>) {
    map.extend(map_ref.into_iter().map(|(k, v)| (k.clone(), v.clone())));
}

fn main() {
    println!("Hello, world!");
}

transform_keys / transform_values

Java

// Guava
Maps.transformEntries();
Maps.transformValues();

Python

alphabet =  {k.lower(): v for k, v in alphabet.items()}

Rust

values

Java

Map#values()

Python

h.values()

Rust

map.values()

values_at

Java

Maps#filterKeys() // Guava

Python

from operator import itemgetter
myvalues = itemgetter(*mykeys)(mydict)

Rust

slice

Java

同 values_at

Python

{k:adict[k] for k in ('key1','key2','key99') if k in adict}

Rust

Strings

% / format

Java

String.format();
System.out.printf();


String template = "Hi ${name}! Your number is ${number}";
Map<String, String> data = new HashMap<String, String>();
data.put("name", "John");
data.put("number", "1");
// commons-lang
String formattedString = StrSubstitutor.replace(template, data)

Python

'hello %s %s' % ('Jack', 'David')

print('%d %s cost $%.2f' % (6, 'bananas', 1.74))

print('{0} {1} cost ${2}'.format(6, 'bananas', 1.74))
print('{quantity} {item} cost ${price}'.format(quantity=6, item='bananas', price=1.74))

Rust

format!("Hello");                 // => "Hello"
format!("Hello, {}!", "world");   // => "Hello, world!"
format!("The number is {}", 1);   // => "The number is 1"
format!("{:?}", (3, 4));          // => "(3, 4)"
format!("{value}", value=4);      // => "4"
format!("{} {}", 1, 2);           // => "1 2"
format!("{:04}", 42);             // => "0042" with leading zeros

*

Java

Strings.repeat(); // Guava
StringUtils.repeat(); // commons-lang

Python

Rust

let y = "hello";
let y = y.repeat(3);

<<

Java

StringBuilder#append()

Python

Rust

let mut y = String::new();
y.push_str("hello");
y.push_str(" ");
y.push_str("world");

=~

Java

String#matches()

Python

bool(re.match(r"hello[0-9]+", 'hello1'))

Rust

let re = Regex::new(r"(?x)
  (?P<y>\d{4}) # the year
  -
  (?P<m>\d{2}) # the month
  -
  (?P<d>\d{2}) # the day
").unwrap();

re.is_match("hello");

bytes

Java

String#getBytes()

Python

"hello".encode()

Rust

"hello".bytes()

capitalize

Java

StringUtils.capitalize(); // commons-lang

Python

'hello'.capitalize()

Rust

chars / codepoints

Java

String#getChars();
CharSequence#chars();
CharSequence#codePoints();

Python

[e for e in 'hello']

Rust

"hello".chars()

chomp!

Java

StringUtils.chomp(); // commons-lang

Python

"hello\n".rstrip('\n')

Rust

trim_* / strip_*

chr / ord

Java

String#codePointAt();
new String();
String.copyValueOf();

Python

chr / ord

Rust

https://docs.rs/asciis/0.1.3/asciis/

upcase / downcase

Java

String#toUpperCase();
String#toLowerCase();

Python

upper / lower

Rust

to_upercase / to_lowercase

each_line / lines

Java

String#split();

new Scanner(str).useDelimiter("\n").forEachRemaining();

CharSource.wrap(str).lines();

Python

'hello\nworld'.splitlines()

Rust

let y = "hello\nworld";
let y = y.lines().collect_vec();

start_with? / end_with?

Java

String#startsWith();
String#endsWith();

Python

starswith / endswith

Rust

starts_with / ends_with

sub / gsub

Java

String#replaceFirst();
String#replaceAll();

Python

re.sub(pattern, repl, string, count=0, flags=0)

Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl. If the pattern isn’t found, string is returned unchanged. repl can be a string or a function; if it is a string, any backslash escapes in it are processed. That is, \n is converted to a single newline character, \r is converted to a carriage return, and so forth. Unknown escapes such as \j are left alone. Backreferences, such as \6, are replaced with the substring matched by group 6 in the pattern. For example:

>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
...        r'static PyObject*\npy_\1(void)\n{',
...        'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'

If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string. For example:

>>> def dashrepl(matchobj):
...     if matchobj.group(0) == '-': return ' '
...     else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

The pattern may be a string or an RE object.

Rust

lazy_static! {
    static ref ISO8601_DATE_REGEX : Regex = Regex::new(
        r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})"
    ).unwrap();
}
ISO8601_DATE_REGEX.replace_all(before, "$m/$d/$y")

inspect

Java

ReflectionToStringBuilder.toString("hello", ToStringStyle.SIMPLE); // commons-lang

Python

repr

Rust

reverse

Java

StringBuffer#reverse();
StringBuilder#reverse();
StringUtils#reverse(); // commons-lang

Python

"hello"[::-1]

Rust

let y = "hello";
let y: String = y.chars().rev().collect();

scan

Java

java.uti.Scanner

Python

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

Rust

let re = Regex::new(r"[a-z]+(?:([0-9]+)|([A-Z]+))").unwrap();
let caps = re.captures("abc123").unwrap();

let text1 = caps.get(1).map_or("", |m| m.as_str());
let text2 = caps.get(2).map_or("", |m| m.as_str());
assert_eq!(text1, "123");
assert_eq!(text2, "");

split

Java

String#split();
java.util.Scanner;

Python

'hello world'.split(' ')

Rust

let y = "hello world";
let y = y.split(" ").collect_vec();

IOs

each / each_line

Java

Files.lines(file).forEach(System.out::println);

Python

filepath = 'Iliad.txt'
with open(filepath) as fp:
   for cnt, line in enumerate(fp):
       print("Line {}: {}".format(cnt, line))

# Using readlines()
file1 = open('myfile.txt', 'r')
lines = file1.readlines()

Rust

let f = File::open("foo.txt")?;
let f = BufReader::new(f);

for line in f.lines() {
    println!("{}", line.unwrap());
}

read / readlines

Files.lines();
Files.readAllLines();
Files.readAllBytes();
FileUtils.readLines();  // commons-io
FileUtils.readFileToString();

basename / dirname

Java

FilenameUtils.getBaseName();
FilenameUtils.getPath();

Python

os.path.basename / os.path.dirname

Rust

use std::path::Path;
use std::ffi::OsStr;

// Note: this example does work on Windows
let path = Path::new("./foo/bar.txt");

let parent = path.parent();
assert_eq!(parent, Some(Path::new("./foo")));

let file_stem = path.file_stem();
assert_eq!(file_stem, Some(OsStr::new("bar")));

let extension = path.extension();
assert_eq!(extension, Some(OsStr::new("txt")));

absolute_path

Java

File#getAbsolutePath();
Path#toAbsolutePath();
FilenameUtils.getFullPath();

Python

os.path.abspath("mydir/myfile.txt")

Rust

use std::fs;
use std::path::PathBuf;

fn main() {
    let srcdir = PathBuf::from("./src");
    println!("{:?}", fs::canonicalize(&srcdir));

    let solardir = PathBuf::from("./../solarized/.");
    println!("{:?}", fs::canonicalize(&solardir));
}

atime / mtime

Java

BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
FileTime time = attrs.lastAccessTime();
FileTime time = attrs.lastModifiedTime();
FileTime time = attrs.lastCreationTime();

File#lastModified();

Python

import os.path, time
print("last modified: %s" % time.ctime(os.path.getmtime(file)))
print("created: %s" % time.ctime(os.path.getctime(file)))

Rust

https://doc.rust-lang.org/std/fs/struct.Metadata.html

chmod / chown

Java

Files.setOwner();
Files.setPosixFilePermissions();

File#setReadable();
File#setWritable();
File#setExecutable();

Python

os.chmod / os.chown

Rust

https://doc.rust-lang.org/std/fs/fn.set_permissions.html

directory?

Java

Files.isDirectory();
File#isDirectory();

Python

os.path.isdir

Rust

use std::fs::metadata;

fn main() {
    let md = metadata(".").unwrap();
    println!("is dir: {}", md.is_dir());
    println!("is file: {}", md.is_file());
}

exists?

Java

Files.exists();
File#exist();

Python

os.path.exists

Rust

use std::path::Path;

fn main() {
    println!("{}", Path::new("/etc/hosts").exists());
}

join

Java

Paths.get(String first, String... more);

Python

os.path.join

Rust

use std::path::{Path, PathBuf};

assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd"));

size

Java

Files.size();
FileUtils.sizeof(); // commons-io

Python

os.path.getsize()

Rust

use std::fs;

fn main() -> std::io::Result<()> {
    let metadata = fs::metadata("foo.txt")?;

    assert_eq!(0, metadata.len());
    Ok(())
}

Active Support

present?

// commons-lang, spring utils
StringUtils#isNotBlank();
StringUtils#isNotEmpty();

// commons-collections, spring utils
CollectionUtils.isNotEmpty();

blank?

// commons-lang, spring utils
StringUtils#isBlank();
StringUtils#isEmpty();

// commons-collections, spring utils
CollectionUtils.isEmpty();

in_groups

in_groups_of

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment