Contents
  1. 1. Lambda
    1. 1.1. Optional
  2. 2. Stream
    1. 2.1. Create Streams
  3. 3. Collections
    1. 3.1. Construction
      1. 3.1.1. Collection Element Type Conversion
      2. 3.1.2. Collection Conversion
      3. 3.1.3. Merge
      4. 3.1.4. Convert List to Tree
      5. 3.1.5. Find Path of Node In a Tree
      6. 3.1.6. Find Descendant Nodes in a Tree
    2. 3.2. Operation
      1. 3.2.1. Traversal
      2. 3.2.2. Handling List piece by piece
      3. 3.2.3. Print collections
      4. 3.2.4. Join
      5. 3.2.5. Remove elements from collection
      6. 3.2.6. Deduplication
      7. 3.2.7. Only one consecutive repeated element is retained
      8. 3.2.8. Ordered Collections
      9. 3.2.9. Reversion
    3. 3.3. Computation
      1. 3.3.1. Reduction
      2. 3.3.2. Group
  4. 4. References

Lambda

Optional

Checking value presence and conditional action

1
2
3
4
5
6
7
8
9
10
11
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
1
list.stream()
  1. Create a stream from specified values

Stream.of(T…t)

1
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
  1. Create a stream from an array
1
2
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.

1
2
Stream<String> streamOfArray
= Stream.empty();
  1. Using Stream.builder()
1
2
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()
1
2
3
Stream.iterate(seedValue, (Integer n) -> n * n)
.limit(limitTerms)
.forEach(System.out::println);
  1. Create an infinite Stream using Stream.generate()
1
2
3
Stream.generate(Math::random)
.limit(limitTerms)
.forEach(System.out::println);
  1. Create stream from Iterator
1
2
3
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
1
2
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

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

by contructor

1
2
3
4
5
6
7
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
8
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));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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

1
2
3
4
// use list.toArray()
User[] users = userList.toArray(new User[0]);
Integer[] integers = integerList.toArray(new Integer[0]);
String[] strings = stringList.toArray(new String[0]);
1
2
3
4
5
6
// 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);
1
2
3
// 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

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

Convert Wrapper Type Array to ArrayList

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

Convert Primitive Array to ArrayList

1
2
3
// use Arrays.stream()
int[] input = new int[]{1,2,3,4};
List<Integer> output = Arrays.stream(input).boxed().collect(Collectors.toList());
1
2
3
// 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

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

Convert Wrapper Type Array to Set

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

Convert other set classes

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

To Map

Convert Object Fields of List to Map

1
2
3
4
5
6
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()));
1
2
3
4
5
6
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

1
2
3
// 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));
1
2
3
// to ConcurrentMap
List<IdName> idNameList = new ArrayList<>();
idNameList.stream().collect(Collectors.toConcurrentMap(IdName::getId, IdName::getName));

Merge

Merge byte[] array

use System.arraycopy

1
2
3
4
5
6
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

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
[{
id: 1,
name: "a",
prarentId: 0
},{
id: 10,
name: "b",
prarentId: 1
},{
id: 2,
name: "c",
prarentId: 0
}]

The process of conversion

1
2
3
4
5
6
7
8
9
10
11
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

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@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

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
27
28
29
30
31
32
33
34
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

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

    1
    2
    3
    4
    5
    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...)

    1
    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

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

List, Set, Map

1
2
3
System.out.println(list);
System.out.println(set);
System.out.println(map);

Join

Use stream

1
2
3
4
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()

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

Remove elements from collection

1
2
3
4
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)
1
2
3
4
5
6
7
8
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)
1
2
3
4
5
6
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)
1
2
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.”
1
2
3
4
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()
1
2
3
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
1
2
3
4
5
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);
1
2
3
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
    1
    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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
[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

1
2
3
Comparator<Employee> compareByFirstName = Comparator.comparing(Employee::getFirstName);
Comparator<Employee> compareByLastName = Comparator.comparing(Employee::getLastName);
Comparator<Employee> compareByFullName = compareByFirstName.thenComparing(compareByLastName);
1
2
3
Comparator<Employee> compareByName = Comparator
.comparing(Employee::getFirstName)
.thenComparing(Employee::getLastName);
1
2
3
4
5
6
7
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

1
2
3
Comparator<Employee> compareByName = Comparator
.comparing(Employee::getFirstName, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Employee::getLastName, Comparator.nullsLast(Comparator.naturalOrder()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()
1
2
3
4
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)

1
2
3
4
5
6
7
8
public class Animal implements Comparable<Animal> {
private String name;

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

Reversion

  1. Using void Collections.reverse(list)
1
2
3
List list = Arrays.asList("a", "b", "c");
Collections.reverse(list);
System.out.println(list);
  1. Using for loop
1
2
3
4
5
6
7
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

stream

1
2
3
// 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);
1
2
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
1
2
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)

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

Group

Group

  • groupingBy()
  • partitioningBy()
1
2
3
4
5
6
7
// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));

// 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()
1
2
3
4
// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
1
2
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));

Sort by grouped fields

1
2
3
4
5
6
// 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)));
1
2
3
4
5
6
7
// 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

Contents
  1. 1. Lambda
    1. 1.1. Optional
  2. 2. Stream
    1. 2.1. Create Streams
  3. 3. Collections
    1. 3.1. Construction
      1. 3.1.1. Collection Element Type Conversion
      2. 3.1.2. Collection Conversion
      3. 3.1.3. Merge
      4. 3.1.4. Convert List to Tree
      5. 3.1.5. Find Path of Node In a Tree
      6. 3.1.6. Find Descendant Nodes in a Tree
    2. 3.2. Operation
      1. 3.2.1. Traversal
      2. 3.2.2. Handling List piece by piece
      3. 3.2.3. Print collections
      4. 3.2.4. Join
      5. 3.2.5. Remove elements from collection
      6. 3.2.6. Deduplication
      7. 3.2.7. Only one consecutive repeated element is retained
      8. 3.2.8. Ordered Collections
      9. 3.2.9. Reversion
    3. 3.3. Computation
      1. 3.3.1. Reduction
      2. 3.3.2. Group
  4. 4. References