Apache Commons代码片段

在Java领域,最流行的的工具库就是Apache Commons和Google Guava [ref]。

Apache Commons,wikicommons.apache.org:Commons包含各种各样的子项目,其中比较流行的有IO, Lang, Logging, Codec, Collections, dbcp等[ref]

  1. Commons IO,homeguidegithubmvnjavadoc:Commons-IO contains utility classes (IOUtils, FileUtils, FilenameUtils, FileSystemUtils), endian classes (EndianUtils), line iterator, file filters (IOFileFilter), file comparators and stream implementations.
  2. Commons Lang,homegithubmvnjavadoc:String manipulation (StringUtils, StringEscapeUtils, RandomStringUtils); Character handling (CharSetUtils, CharSet, CharRange, CharUtils); JVM interaction (SystemUtils, CharEncoding); Serialization (SerializationUtils, SerializationException); Assorted functions (ObjectUtils, ClassUtils, ArrayUtils, BooleanUtils),使用,如jsoup库[ref]
  3. Commons Collections,homejavadoc:或者Google Guava
  4. Commons BeanUtils,homejavadoc
  5. HttpClient,homemvn:2007年,旧的Commons HttpClient 3.x升级为Apache HttpClient 4.x[ref]
  6. Commons Logging,home:可以使用SLF4J替代
  7. 其他

Apache Commons下的库,部分文档比较简陋,或者过时没有及时更新。部分功能的使用需要反复翻javadoc才搞明白用法。本文是,平时本人在使用BeanUtils和Collections中,一些典型用法的总结。

Commons BeanUtils

关于BeanUtils的使用,官方的包描述有好介绍。这里记录部分常见的用法。
Bean的定义(为了使代码更加简洁,这里使用了Lombok):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lombok.Data;

@Data
public class Person {
private Integer id;
private Name name;
private String[] address;

public Person() {
}

public Person(Integer id, Name name, String[] address) {
this.id = id;
this.name = name;
this.address = address;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import lombok.Data;

@Data
public class Name {
private String first;
private String last;

public Name(String first, String last) {
this.first = first;
this.last = last;
}
}
1
2
3
4
5
6
7
8
9
import lombok.Data;
import java.util.List;
import java.util.Map;

@Data
public class PersonWrapper {
private List<Person> list;
private Map<String, Person> map;
}

访问Bean属性

涉及到的类方法:PropertyUtils.getSimpleProperty、PropertyUtils.getIndexedProperty、PropertyUtils.getNestedProperty、PropertyUtils.getMappedProperty和PropertyUtils.getProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Person person = new Person(1, new Name("Bill", "Gates"), new String[]{"北京", "上海"});
System.out.println((Integer) PropertyUtils.getSimpleProperty(person, "id"));
PropertyUtils.setSimpleProperty(person, "id", 42);
System.out.println(person.getId());
System.out.println((String) PropertyUtils.getIndexedProperty(person, "address[1]"));
System.out.println((String) PropertyUtils.getNestedProperty(person, "name.first"));

Person person2 = new Person(2, new Name("Steve", "Jobs"), new String[]{"纽约", "洛杉矶"});
PersonWrapper personWrapper = new PersonWrapper();
personWrapper.setList(Arrays.asList(person, person2));
Map<String, Person> map = new HashMap<>();
map.put("person", person);
map.put("person2", person2);
personWrapper.setMap(map);
System.out.println((Person) PropertyUtils.getMappedProperty(personWrapper, "map(person)"));
System.out.println((String) PropertyUtils.getProperty(personWrapper, "map(person2).address[1]"));

输出:

1
2
3
4
5
6
1
42
上海
Bill
Person(id=42, name=Name(first=Bill, last=Gates), address=[北京, 上海])
洛杉矶

复制Bean属性

需要注意的点:

1
2
3
4
5
6
7
import lombok.Data;

@Data
public class Person2 {
private Integer id;
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
Person person = new Person(1, new Name("Bill", "Gates"), new String[]{"北京", "上海"});
Person person2 = (Person) BeanUtils.cloneBean(person);
System.out.println(person2);
System.out.println(person2.getName() == person.getName()); // 浅拷贝

Person person3 = new Person();
PropertyUtils.copyProperties(person3, person);
System.out.println(person3);

Person2 person4 = new Person2();
BeanUtils.copyProperties(person4, person); // 属性的类型不同将尝试自动转换
System.out.println(person4.getName());

输出:

1
2
3
4
Person(id=1, name=Name(first=Bill, last=Gates), address=[北京, 上海])
true
Person(id=1, name=Name(first=Bill, last=Gates), address=[北京, 上海])
Name(first=Bill, last=Gates)

Commons Collections

Commons Collections版本4之后加入泛型的支持,但官方介绍文档没有及时更新。这里记录部分常见的用法。

1
2
3
4
5
6
7
8
9
10
11
12
import lombok.Data;

@Data
public class Hello {
private Integer id;
private String name;

public Hello(Integer id, String name) {
this.id = id;
this.name = name;
}
}

列表中提取属性

涉及到的类方法:CollectionUtils.collect、TransformerUtils.invokerTransformer。同时包含Java 8的写法,Stream.collect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.TransformerUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class CollectionTest {

public static void main(String[] args) {
Hello h1 = new Hello(1, "Andy");
Hello h2 = new Hello(3, "Cell");
Hello h3 = new Hello(2, "Bill");
Hello h4 = new Hello(2, "Billy");
List<Hello> list = new ArrayList<>(Arrays.asList(h1, h2, h3, h4));

Collection<String> nameList1 = CollectionUtils.collect(list, TransformerUtils.<Hello, String>invokerTransformer("getName"));
List<String> nameList2 = list.stream().map(Hello::getName).collect(Collectors.toList());
System.out.println(StringUtils.join(nameList1));
System.out.println(StringUtils.join(nameList2));
}
}

输出:

1
2
[Andy, Cell, Bill, Billy]
[Andy, Cell, Bill, Billy]

列表筛选

涉及的类方法:CollectionUtils.select

1
2
3
4
5
6
7
8
9
10
Collection<Hello> result1 = CollectionUtils.select(list, PredicateUtils.<Hello>equalPredicate(h2));
System.out.println(StringUtils.join(result1));

Collection<Hello> result2 = CollectionUtils.select(list, new Predicate<Hello>() {
@Override
public boolean evaluate(Hello object) {
return object.getName().startsWith("B");
}
});
System.out.println(StringUtils.join(result2));

输出:

1
2
[Hello(id=3, name=Cell)]
[Hello(id=2, name=Bill), Hello(id=2, name=Billy)]

分割成多个子列表

涉及的类方法:ListUtils.partition

1
2
3
4
List<List<Hello>> subLists = ListUtils.partition(list, 3);
for (List<Hello> subList : subLists) {
System.out.println(StringUtils.join(subList));
}

输出:

1
2
[Hello(id=1, name=Andy), Hello(id=3, name=Cell), Hello(id=2, name=Bill)]
[Hello(id=2, name=Billy)]

列表排序

包含倒序、多级排序、null处理的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Collections.sort(list, new BeanComparator<>("id"));
System.out.println(StringUtils.join(list));

Collections.sort(list, new BeanComparator<>("id", Comparator.reverseOrder())); // 倒序
System.out.println(StringUtils.join(list));

ComparatorChain<Hello> comparatorChain = new ComparatorChain<>();
comparatorChain.addComparator(new BeanComparator<>("id"));
comparatorChain.addComparator(new BeanComparator<>("name"), true); // 倒序
Collections.sort(list, comparatorChain);
System.out.println(StringUtils.join(list));

list.add(new Hello(null, "Bill"));
Collections.sort(list, new BeanComparator<>("id", new NullComparator(false))); // null排最前
System.out.println(StringUtils.join(list));

输出:

1
2
3
4
[Hello(id=1, name=Andy), Hello(id=2, name=Bill), Hello(id=2, name=Billy), Hello(id=3, name=Cell)]
[Hello(id=3, name=Cell), Hello(id=2, name=Bill), Hello(id=2, name=Billy), Hello(id=1, name=Andy)]
[Hello(id=1, name=Andy), Hello(id=2, name=Billy), Hello(id=2, name=Bill), Hello(id=3, name=Cell)]
[Hello(id=null, name=bill), Hello(id=1, name=Andy), Hello(id=2, name=Billy), Hello(id=2, name=Bill), Hello(id=3, name=Cell)]​