Java Code Examples: Collection and Stream

Lambda

Optional

Checking value presence and conditional action

SysUser user = new SysUser();
SysDept dept = new SysDept();
dept.setDeptName("development");
user.setDept(dept);
Optional<String> optional = Optional.ofNullable(user)
.map(SysUser::getDept)
.map(SysDept::getDeptName);
optional.ifPresent(System.out::println);
String deptName = optional.orElse("default");
String deptName = optional.orElseGet(() -> "to get default");
optional.orElseThrow(() -> new RuntimeException("to throw exception"));

Stream

Create Streams

  1. Using collection
list.stream()
  1. Create a stream from specified values

Stream.of(T…t)

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
  1. Create a stream from an array
Arrays.stream(arr);
Stream.of(arr);
  1. Create an empty stream using Stream.empty()

The empty() method is used upon creation to avoid returning null for streams with no element.

Stream<String> streamOfArray
= Stream.empty();
  1. Using Stream.builder()
Stream.Builder<String> builder = Stream.builder();
Stream<String> stream = builder.add("a").add("b").add("c").build();
  1. Create an infinite Stream using Stream.iterate()
Stream.iterate(seedValue, (Integer n) -> n * n)
.limit(limitTerms)
.forEach(System.out::println);
  1. Create an infinite Stream using Stream.generate()
Stream.generate(Math::random)
.limit(limitTerms)
.forEach(System.out::println);
  1. Create stream from Iterator
Iterator<String> iterator = Arrays.asList("a", "b", "c").iterator();
Spliterator<T> spitr = Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL);
Stream<T> stream = StreamSupport.stream(spitr, false);
  1. Create stream from Iterable
Iterable<String> iterable = Arrays.asList("a", "b", "c");
Stream<T> stream = StreamSupport.stream(iterable.spliterator(), false);

Collections

Construction

Collection Element Type Conversion

String to Object

by assignment

// only array
String[] stringArray = new String[10];
Object[] objectArray = stringArray;

by contructor

// list
List<String> stringList = new ArrayList<>();
List<Object> objectList = new ArrayList<>(stringList);

// set
Set<String> stringSet = new HashSet<>();
Set<Object> objectSet = new HashSet<>(stringSet);

by for loop

// multiStringValueMap to multiObjectValueMap
Map<String, List<String>> multiStringValueMap = new HashMap<>();
multiStringValueMap.put("key1", Arrays.asList("taogen", "taogen2"));
multiStringValueMap.put(null, Arrays.asList(null, null, null));
multiStringValueMap.put("testNullValue", null);
Map<String, List<Object>> multiObjectValueMap = new HashMap<>();
multiStringValueMap.forEach((key, value) -> {
List<Object> objectList = null;
if (value != null) {
objectList = value.stream()
.collect(Collectors.toList());
}
multiObjectValueMap.put(key, objectList);
});
System.out.println(multiObjectValueMap);

Object to String

by Java Stream

List<Object> objectList = new ArrayList<>();
List<String> stringList = objectList.stream()
.map(object -> Objects.toString(object, null))
.collect(Collectors.toList());
for (Object s : objectList) {
System.out.println(s + ", isNull: " + Objects.isNull(s));
}

by for loop

List<Object> objectList = new ArrayList<>();
List<String> stringList = new ArrayList<>(objectList.size());
for (Object object : objectList) {
stringList.add(Objects.toString(object, null));
}
for (Object s : objectList) {
System.out.println(s + ", isNull: " + Objects.isNull(s));
}
// multiObjectValueMap to multiStringValueMap
Map<String, List<Object>> multiObjectValueMap = new HashMap<>();
multiObjectValueMap.put("key1", Arrays.asList(1, 2, 3));
multiObjectValueMap.put(null, Arrays.asList(null, null, null));
multiObjectValueMap.put("testNullValue", null);
multiObjectValueMap.put("key2", Arrays.asList("taogen", "taogen2"));
Map<String, List<String>> multiStringValueMap = new HashMap<>();
multiObjectValueMap.forEach((key, value) -> {
List<String> stringList = null;
if (value != null) {
stringList = value.stream()
.map(object -> Objects.toString(object, null))
.collect(Collectors.toList());
}
multiStringValueMap.put(key, stringList);
});
System.out.println(multiStringValueMap);

Warning: to convert a object value to a string value you can use Objects.toString(object) or object != null ? object.toString() : null. but not String.valueOf() and toString(). The result of String.valueOf(null) is “null” not null. If the object value is null, calling toString() will occur NullPointerExcpetion.

Collection Conversion

To Array

Object list to array

// use list.toArray()
User[] users = userList.toArray(new User[0]);
Integer[] integers = integerList.toArray(new Integer[0]);
String[] strings = stringList.toArray(new String[0]);
// use Java 8 stream
User[] users = userList.stream().toArray(User[]::new);
Integer[] integers = integerList.stream().toArray(Integer[]::new);
String[] strings = stringList.stream().toArray(String[]::new);
// Java 11
String[] strings = stringList.toArray(String[]::new);
// use for loop
int[] array = new int[list.size()];
for(int i = 0; i < list.size(); i++) array[i] = list.get(i);

To ArrayList

Convert Set to ArrayList

Set<String> set = new HashSet();
ArrayList<String> list = new ArrayList(set);
list.addAll(set);
// Java 8
List<String> list = set.stream().collect(Collectors.toList());
// Java 10
var list = List.copyOf(set);

Convert Wrapper Type Array to ArrayList

String[] array = new String[10];
Integer[] array2 = new Integer[10];
ArrayList<String> list = new ArrayList(Arrays.asList(array));

Convert Primitive Array to ArrayList

// use Arrays.stream()
int[] input = new int[]{1,2,3,4};
List<Integer> output = Arrays.stream(input).boxed().collect(Collectors.toList());
// use IntStream.of()
int[] input = new int[]{1,2,3,4};
List<Integer> output = IntStream.of(input).boxed().collect(Collectors.toList());

To Set

Convert ArrayList to Set

ArrayList<String> list = new ArrayList();
Set<String> set = new HashSet(list);
set.addAll(aList);
// Java 8
Set<String> set = list.stream().collect(Collectors.toSet());
// Java 10
var set = Set.copyOf(list);

Convert Wrapper Type Array to Set

String[] array = new String[10];
Set<String> set = new HashSet(Arrays.asList(array));

Convert other set classes

// to LinkedHashSet
list.stream().collect(Collectors.toCollection(LinkedHashSet::new))

To Map

Convert Object Fields of List to Map

List<SysUser> sysUserList = getUserList();
Map<Long, String> idToName = sysUserList.stream()
.collect(Collectors.toMap(SysUser::getId, SysUser::getName));
// or
Map<Long, String> idToName = sysUserList.stream()
.collect(Collectors.toMap(item -> item.getId(), item -> item.getName()));
List<IdName> list = getIdNameList();
Map<Long, IdName> idToObjMap = list.stream()
.collect(Collectors.toMap(IdName::getId, Function.identity()));
// or
Map<Long, IdName> idToObjMap = list.stream()
.collect(Collectors.toMap(item -> item.getId(), item -> item));

Convert to other map classes

// to TreeMap
List<IdName> idNameList = new ArrayList<>();
Map<Integer,String> idToNameMap = idNameList.stream().collect(Collectors.toMap(IdName::getId, IdName::getName, (o1, o2) -> o1, TreeMap::new));
// to ConcurrentMap
List<IdName> idNameList = new ArrayList<>();
idNameList.stream().collect(Collectors.toConcurrentMap(IdName::getId, IdName::getName));

Merge

Merge byte[] array

use System.arraycopy

byte[] one = getBytesForOne();
byte[] two = getBytesForTwo();
byte[] combined = new byte[one.length + two.length];

System.arraycopy(one,0,combined,0 ,one.length);
System.arraycopy(two,0,combined,one.length,two.length);

Use List

byte[] one = getBytesForOne();
byte[] two = getBytesForTwo();

List<Byte> list = new ArrayList<Byte>(Arrays.<Byte>asList(one));
list.addAll(Arrays.<Byte>asList(two));

byte[] combined = list.toArray(new byte[list.size()]);

Use ByteBuffer

byte[] allByteArray = new byte[one.length + two.length + three.length];

ByteBuffer buff = ByteBuffer.wrap(allByteArray);
buff.put(one);
buff.put(two);
buff.put(three);

byte[] combined = buff.array();

Convert List to Tree

convert list to tree with parentId

The data

[{
id: 1,
name: "a",
prarentId: 0
},{
id: 10,
name: "b",
prarentId: 1
},{
id: 2,
name: "c",
prarentId: 0
}]

The process of conversion

1. original list
a
b
c
2. link children and mark first level nodes
*a -> b
b
*c
3. get first level nodes
a -> b
c

Implementation

@Data
public class IdName {
private String id;
private String name;
private String parentId;
private List<IdName> children;

public IdName(String id, String name, String parentId) {
this.id = id;
this.name = name;
this.parentId = parentId;
}

private void putChildren(IdName idName) {
if (this.children == null) {
this.children = new ArrayList<>();
}
this.children.add(idName);
}

public static List<IdName> convertListToTree(List<IdName> list) {
if (list == null || list.isEmpty()) {
return Collections.emptyList();
}
Map<String, IdName> map = list.stream()
.collect(Collectors.toMap(IdName::getId, Function.identity()));
List<IdName> firstLevelNodeList = new ArrayList<>();
for (IdName idName : list) {
IdName parent = map.get(String.valueOf(idName.getParentId()));
if (parent != null) {
parent.putChildren(idName);
} else {
firstLevelNodeList.add(idName);
}
}
return firstLevelNodeList;
}
}

public static void main(String[] args) {
List<IdName> idNames = new ArrayList<>();
idNames.add(new IdName("1", "Jack", "0"));
idNames.add(new IdName("2", "Tom", "0"));
idNames.add(new IdName("3", "Jerry", "1"));
System.out.println(IdName.convertListToTree(idNames));
}

Multiple level data is in multiple tables

public List<AreaVo> getAreaVoList() {
List<Province> provinces = iProvinceService.list(
new LambdaQueryWrapper<Province>()
.select(Province::getId, Province::getName, Province::getCode));
List<City> cities = iCityService.list(
new LambdaQueryWrapper<City>()
.select(City::getId, City::getName, City::getCode, City::getProvinceCode));
List<County> counties = iCountyService.list(
new LambdaQueryWrapper<County>()
.select(County::getId, County::getName, County::getCode, County::getCityCode));
List<AreaVo> resultList = new ArrayList<>();
resultList.addAll(AreaVo.fromProvince(provinces));
resultList.addAll(AreaVo.fromCity(cities));
resultList.addAll(AreaVo.fromCounty(counties));
return AreaVo.convertListToTree(resultList);
}

public class AreaVo {
private String label;
private String value;
private String parentId;
private List<AreaVo> children;

public static List<AreaVo> fromProvince(List<Province> provinces) {
if (provinces == null || provinces.isEmpty()) {
return Collections.emptyList();
}
return provinces.stream()
.map(item -> new AreaVo(item.getName(), item.getCode(), "0"))
.sorted(Comparator.comparing(AreaVo::getValue))
.collect(Collectors.toList());
}
public static List<AreaVo> convertListToTree(List<AreaVo> list) {}
}

Find Path of Node In a Tree

private void setAreaForList(List<User> records) {
List<String> areaCodeList = records.stream()
.map(User::getAreaId)
.filter(Objects::nonNull)
.map(String::valueOf)
.collect(Collectors.toList());

List<AreaItem> areaItemList = iAreaService.findSelfAndAncestors(areaCodeList);
if (areaItemList == null || areaItemList.isEmpty()) {
return;
}
Map<String, AreaItem> areaItemMap = areaItemList.stream()
.collect(Collectors.toMap(AreaItem::getCode, Function.identity()));
records.stream()
.filter(entity -> entity.getAreaId() != null)
.forEach(entity -> {
List<AreaItem> areaPath = new ArrayList<>();
String areaCode = entity.getAreaId().toString();
String tempCode = areaCode;
AreaItem areaItem = null;
while ((areaItem = areaItemMap.get(tempCode)) != null) {
areaPath.add(0, areaItem);
tempCode = areaItem.getParentCode();
}
if (CollectionUtils.isEmpty(areaPath)) {
return;
}
int totalLevel = 2;
if (areaPath.size() < totalLevel) {
int supplementSize = totalLevel - areaPath.size();
for (int i = 0; i < supplementSize; i++) {
areaPath.add(null);
}
} else {
areaPath = areaPath.subList(0, totalLevel);
}
entity.setAreaPath(areaPath);
entity.setAreaArray(areaPath.stream()
.map(areaPathItem -> areaPathItem == null ? null : areaPathItem.getCode())
.collect(Collectors.toList()));
});
}

Multiple level data is in multiple tables

public List<AreaItem> findSelfAndAncestors(List<String> areaCodeList) {
if (CollectionUtils.isEmpty(areaCodeList)) {
return Collections.emptyList();
}
List<String> tempCodeList = areaCodeList;
List<AreaItem> resultAreaList = new ArrayList<>();
List<AreaCounty> countyList = iAreaCountyService.list(
new LambdaQueryWrapper<AreaCounty>()
.select(AreaCounty::getCode, AreaCounty::getName, AreaCounty::getCityCode)
.in(AreaCounty::getCode, tempCodeList));
if (!CollectionUtils.isEmpty(countyList)) {
AreaItem.fromAreaCounty(countyList)
.forEach(areaItem -> {
resultAreaList.add(areaItem);
areaItem.setLevel(3);
});
tempCodeList = areaCodeList;
tempCodeList.addAll(countyList.stream()
.map(AreaCounty::getCityCode)
.collect(Collectors.toList()));
}
List<AreaCity> cityList = iAreaCityService.list(
new LambdaQueryWrapper<AreaCity>()
.select(AreaCity::getCode, AreaCity::getName, AreaCity::getProvinceCode)
.in(AreaCity::getCode, tempCodeList));
if (!CollectionUtils.isEmpty(cityList)) {
AreaItem.fromAreaCity(cityList)
.forEach(areaItem -> {
resultAreaList.add(areaItem);
areaItem.setLevel(2);
});
tempCodeList = areaCodeList;
tempCodeList.addAll(cityList.stream()
.map(AreaCity::getProvinceCode)
.collect(Collectors.toList()));
}
List<AreaProvince> provinceList = iAreaProvinceService.list(
new LambdaQueryWrapper<AreaProvince>()
.select(AreaProvince::getCode, AreaProvince::getName)
.in(AreaProvince::getCode, tempCodeList));
if (!CollectionUtils.isEmpty(provinceList)) {
AreaItem.fromAreaProvince(provinceList)
.forEach(areaItem -> {
resultAreaList.add(areaItem);
areaItem.setLevel(1);
});
}
return resultAreaList;
}

Find Descendant Nodes in a Tree

Find self and descendant list

private List<User> findSelfAndDescendants(Integer parentId){
List<User> resultList = new ArrayList<>();
List<Integer> tempIds = new ArrayList<>();
tempIds.add(parentId);
List<User> descendants = null;
while (!CollectionUtils.isEmpty(descendants = getListByIds(tempIds))) {
resultList.addAll(descendants);
tempIds.clear();
tempIds = descendants.stream().map(User::getId).collect(Collectors.toList());
}
return resultList;
}
public List<User> getListByIds(List<Integer> ids){}

Find descendant list

private List<User> findDescendants(Integer parentId){
List<User> resultList = new ArrayList<>();
List<Integer> tempIds = new ArrayList<>();
tempIds.add(parentId);
List<User> descendants = null;
while (!CollectionUtils.isEmpty(descendants = getListByParentIds(tempIds))) {
resultList.addAll(descendants);
tempIds.clear();
tempIds = descendants.stream().map(User::getId).collect(Collectors.toList());
}
return resultList;
}
public List<User> getListByParentIds(List<Integer> tempIds){}

Find self and descendant ids

private List<Integer> findSelfAndDescendantIds(Integer parentId){
List<Integer> resultIds = new ArrayList<>();
resultIds.add(id);
List<Integer> tempIds = new ArrayList<>();
tempIds.add(parentId);
List<Integer> childrenIds = null;
while (!CollectionUtils.isEmpty(childrenIds = getChildrenIdsByParentIds(tempIds))) {
resultIds.addAll(childrenIds);
tempIds.clear();
tempIds.addAll(childrenIds);
}
return resultIds;
}
public List<Integer> getChildrenIdsByParentIds(List<Integer> parentIds){}
public Set<Integer> getDescendantIds(Integer deptId) {
List<Object> descendantIds = new ArrayList<>();
List<Object> childIds = this.baseMapper.selectObjs(new LambdaQueryWrapper<SysDept>()
.select(SysDept::getDeptId)
.eq(SysDept::getParentId, deptId));
while (!CollectionUtils.isEmpty(childIds)) {
descendantIds.add(childIds);
childIds = this.baseMapper.selectObjs(new LambdaQueryWrapper<SysDept>()
.select(SysDept::getDeptId)
.in(SysDept::getParentId, childIds));
}
return descendantIds.stream()
.map(Objects::toString)
.filter(Objects::nonNull)
.map(Integer::valueOf)
.collect(Collectors.toSet());
}

Operation

Traversal

Array Traversal

  • for (int i = 0; i < array.length; i++) {...}
  • Arrays.stream(array).xxx

List Traversal

  • for loop: for (int i = 0; i < list.size(); i++) {...}
  • enhanced for loop: for (Object o : list) {...}
  • iterator or listIterator
  • list.forEach(comsumer...)
  • list.stream().xxx

Handling List piece by piece

public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int listSize = list.size();
int handlingSize = 3;
int startIndex = 0;
int endIndex = handlingSize;
while (startIndex < listSize) {
if (endIndex > listSize) {
endIndex = listSize;
}
handleList(list, startIndex, endIndex);
startIndex = endIndex;
endIndex = startIndex + handlingSize;
}
}
private static void handleList(List<Integer> list, int start, int end) {
for (int i = start; i < end; i++) {
System.out.println(list.get(i));
}
}

Map Traversal

  • for (String key : map.keySet()) {...}

  • for (Map.entry entry : map.entrySet()) {...}

  • Iterator

    Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    System.out.println(entry.getKey() + ":" + entry.getValue());
    }
  • map.forEach(biComsumer...)

    map.forEach((k, v) -> System.out.println((k + ":" + v)));
  • map.entrySet().stream()

forEach() vs stream()

  • If you just want to consume list, you best to choose forEach(), else stream().

Array

int a[] = new int[]{1, 2, 3};
System.out.println(Arrays.toString(a));

List, Set, Map

System.out.println(list);
System.out.println(set);
System.out.println(map);

Join

Use stream

List<String> names = Arrays.asList("Tom", "Jack", "Lucy");
System.out.println(names.stream().map(Object::toString).collect(Collectors.joining(",")));
List<Integer> ids = Arrays.asList(1, 2, 3);
System.out.println(ids.stream().map(Object::toString).collect(Collectors.joining(",")));

Use String.join()

List<String> names = Arrays.asList("Tom", "Jack", "Lucy");
System.out.println(String.join(",", names));

Remove elements from collection

List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
  1. Collect objects set and removeAll()
  • Swap position of elements and create new collection copy from updated old collection. Don’t reorganize the collection.
  • T(n) = O(n), S(n) = O(n)
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);

1.2 Collect indexes and remove one by one

  • T(n) = O(n), S(n) = O(m * n)

1.3 Collect objects set and remove one by one

  • T(n) = O(n), S(n) = O(m * n)
  1. Using iterator to remove in loop
  • Iterator using the cursor variable to traverse collection and remove by index of collection. If you remove a element, the cursor will update correctly. Iterator like forEach, but it index is not from 0 to size-1 of collection. The every remove operations will creating a new collection that copy from updated old collection.
  • T(n) = O(n), S(n) = O(m * n)
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
  1. removeIf() method (JDK 8)
  • Swap position of elements, set new size for collection, and set null for between new size to old size elements.
  • T(n) = O(n), S(n) = O(1)
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
  1. Using filter of Stream API (JDK 8)
  • Creating new collection. Traversing has no order.
  • T(n) = O(n), S(n) = O(n) guess by “A stream does not store data and, in that sense, is not a data structure. It also never modifies the underlying data source.”
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());

Recommend: removeIf() > stream().filter() or parallelStream()> Collect objects set and removeAll() > Using iterator, or Collect indexes and remove one by one, or Collect objects set and remove one by one.

Deduplication

Deduplicate values

  1. Deduplicate values by stream distinct()
List<Integer> list = Arrays.asList(1, 2, 3, 2, 3, 4);
list = list.stream().distinct().collect(Collectors.toList());
System.out.println(list);
  1. Deduplicate values by creating a set
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 2, 3, 4));
Set<Integer> set = new LinkedHashSet<>(list);
list.clear(); // note: clear Arrays.asList will throw UnsupportedOperationException
list.addAll(set);
System.out.println(list);
List<Integer> list = Arrays.asList(1, 2, 3, 2, 3, 4);
list = new ArrayList<>(new LinkedHashSet<>(list));
System.out.println(list);

Deduplicate objects by property

  1. Deduplicate by stream
    List<User> list = list.stream().collect(Collectors.toMap(User::getName, Function.identity(), (p, q) -> p, LinkedHashMap::new)).values();
  2. Deduplicate objects by removing in for loop
    List<User> userList = buildUserList();
    System.out.println("Before: " + userList);
    Iterator<User> i = userList.iterator();
    while (i.hasNext()) {
    User user = i.next();
    if (user.getUserName().contains("test")) {
    i.remove();
    }
    }
    System.out.println("After: " + userList);
  3. Deduplicate objects by finding duplicate objects and then removing all of it
List<User> userList = buildUserList();
System.out.println("Before: " + userList);
List<User> toRemoveList = new ArrayList<>();
for (User user : userList) {
if (user.getUserName().contains("test")) {
toRemoveList.add(user);
}
}
userList.removeAll(toRemoveList);
System.out.println("After: " + userList);

Only one consecutive repeated element is retained

List<IdName> list = new ArrayList<>();
list.add(new IdName(1, "a"));
list.add(new IdName(2, "a"));
list.add(new IdName(3, "a"));
list.add(new IdName(4, "b"));
list.add(new IdName(5, "b"));
list.add(new IdName(6, "c"));
List<Integer> indexToRemove = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (i < list.size() - 1 && list.get(i).getName().equals(list.get(i + 1).getName())) {
indexToRemove.add(i);
}
}
for (int i = indexToRemove.size() - 1; i >= 0; i--) {
list.remove(indexToRemove.get(i).intValue());
}
System.out.println(list);

Output

[IdName(id=3, name=a), IdName(id=5, name=b), IdName(id=6, name=c)]

Ordered Collections

  1. Sorted Collection Classes
  • TreeSet
  • TreeMap
  1. Inserted order Collection Classes
  • LinkedList
  • LinkedHashSet
  • LinkedHashMap

Sorting

  1. Using Collections.sort(list) to sort Comparable elements

It uses merge sort. T(n) = O(log n)

  • sort(List<T> list)
  • sort(List<T> list, Comparator c)

Comparators

  • Comparator.naturalOrder()
  • Comparator.comparing(Function f)
  • Comparator.comparingInt(Function f)
  • Collections.reverseOrder()
  • Collections.reverseOrder(Comparator c)

(o1, o2) -> o1.getType().compareTo(o2.getType()) equals Comparator.comparing(User::getType)

Multiple fields with comparator

Comparator<Employee> compareByFirstName = Comparator.comparing(Employee::getFirstName);
Comparator<Employee> compareByLastName = Comparator.comparing(Employee::getLastName);
Comparator<Employee> compareByFullName = compareByFirstName.thenComparing(compareByLastName);
Comparator<Employee> compareByName = Comparator
.comparing(Employee::getFirstName)
.thenComparing(Employee::getLastName);
Comparator<IdName> c = (o1, o2) -> {
int i = o1.getFirstName().compareTo(o2.getFirstName());
if (i != 0) {
return i;
}
return o1.getLastName().compareTo(o2.getLastName());
};

Comparators avoid NullPointerException

Comparator<Employee> compareByName = Comparator
.comparing(Employee::getFirstName, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Employee::getLastName, Comparator.nullsLast(Comparator.naturalOrder()));
Comparator<IdName> c = (o1, o2) -> {
// nullsLast
if (o1.getId() == null) {
return 1;
} else if (o2.getId() == null) {
return -1;
}
int i = o1.getId().compareTo(o2.getId());
if (i != 0) {
return i;
}
// nullsLast
if (o1.getName() == null) {
return 1;
} else if (o2.getName() == null) {
return -1;
}
return o1.getName().compareTo(o2.getName());
};
  1. Stream.sorted()
List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 2, 6, 5, 4, 9, 7));
list.stream().sorted().forEachOrdered(System.out::print);
list.stream().sorted((o1, o2) -> o1 - o2).forEachOrdered(System.out::print);
list.stream().sorted(Comparator.comparingInt(o -> o)).forEachOrdered(System.out::print);

Summary: if you don’t need to keep collections always be ordered, you just use Collections sort() to get sorted collections.

Compare object list using Collections.sort(objectList)

public class Animal implements Comparable<Animal> {
private String name;

@Override
public int compareTo(Animal o) {
return this.name.compareTo(o.name);
}
}
List<Animal> list = new ArrayList<>();
Collections.sort(list);
Collections.sort(list, Collections.reverseOrder());

Reversion

  1. Using void Collections.reverse(list)
List list = Arrays.asList("a", "b", "c");
Collections.reverse(list);
System.out.println(list);
  1. Using for loop
List<String> list = Arrays.asList("a", "b", "c");
for (int i = 0; i < list.size() / 2; i++) {
String temp = list.get(i);
list.set(i, list.get(list.size() - i - 1));
list.set(list.size() - i - 1, temp);
}
System.out.println(list);
  1. Using recursion
private static void reverse(List<String> list) {
if (list == null || list.size() <= 1) {
return;
}
String value = list.remove(0);
reverse(list);
list.add(value);
}

public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
reverse(list);
System.out.println(list);
}

Computation

Reduction

for loop

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (int x : numbers) {
sum += x;
}

stream

// the first x is 0, the first y is 1
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, (x, y) -> x + y);
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));

parallel stream (operations can run safely in parallel with almost no modification)

int sum = numbers.parallelStream().reduce(0, Integer::sum);

Group

Group

  • groupingBy()
  • partitioningBy()
// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));

// get user's roleIds
Map<Integer, Set<Integer>> userToRoleIds = userRoles.stream().collect(Collectors.groupingBy(UserRole::getUserId, Collectors.mapping(AssignmentDept::getRoleId, Collectors.toSet())));

// Partition students into passing and failing
Map<Boolean, List<Student>> passingFailing = students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

Aggregation

  • maxBy()
  • minBy()
  • averagingInt()
  • summingInt()
  • counting()
// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));

Sort by grouped fields

// keep sorted when group. Using `TreeMap::new` or `() -> new TreeMap<>()`
Map<String, Double> averageAgeByType = userList
.stream()
.collect(Collectors.groupingBy(User::getType,
TreeMap::new,
Collectors.averagingInt(User::getAge)));
// sort list before group and keep insertion order when group
Map<String, Double> userAverageAgeMap2 = userList
.stream()
.sorted(Comparator.comparing(User::getType))
.collect(Collectors.groupingBy(User::getType,
LinkedHashMap::new,
Collectors.averagingInt(User::getAge)));

References