Master Java 8 Stream API: From Basics to Tree Structures

This article introduces Java 8's Stream API, explains core concepts like streams, sources, and aggregation operations, and demonstrates common methods such as filter, map, limit, count, sorted, skip, and collect with practical code examples that build hierarchical permission trees.

macrozheng
macrozheng
macrozheng
Master Java 8 Stream API: From Basics to Tree Structures
Java 8 introduced a brand‑new Stream API that allows declarative data processing, greatly simplifying collection operations with less code.

What Is a Stream?

A Stream is a sequence of elements sourced from a data provider that supports aggregation operations.

Data source: the origin of the stream, e.g., a List used to create a Stream.

Aggregation operation: actions like filter, map, limit, sorted that produce a result based on defined rules.

Stream Aggregation Operations

Background

We use the UmsPermission object from a mall project as an example. It represents a permission with three types: directory, menu, and button.

public class UmsPermission implements Serializable {
    private Long id;
    private Long pid;
    private String name;
    private String value;
    private String icon;
    private Integer type;
    private String uri;
    private Integer status;
    private Date createTime;
    private Integer sort;
    private static final long serialVersionUID = 1L;
    // getters and setters omitted
}

Creating Stream Objects

Streams can be sequential or parallel.

// permissionList is the list of all permissions
Stream<UmsPermission> stream = permissionList.stream(); // sequential
Stream<UmsPermission> parallelStream = permissionList.parallelStream(); // parallel

filter

Filters elements that satisfy a predicate.

List<UmsPermission> dirList = permissionList.stream()
    .filter(p -> p.getType() == 0)
    .collect(Collectors.toList());

map

Transforms each element, e.g., extracting IDs.

List<Long> idList = permissionList.stream()
    .map(p -> p.getId())
    .collect(Collectors.toList());

limit

Gets a fixed number of elements.

List<UmsPermission> firstFiveList = permissionList.stream()
    .limit(5)
    .collect(Collectors.toList());

count

Returns the number of elements.

long dirPermissionCount = permissionList.stream()
    .filter(p -> p.getType() == 0)
    .count();

sorted

Sorts elements according to a comparator.

List<UmsPermission> sortedList = permissionList.stream()
    .sorted((p1, p2) -> p1.getType().compareTo(p2.getType()))
    .collect(Collectors.toList());

skip

Skips a given number of elements.

List<UmsPermission> skipList = permissionList.stream()
    .skip(5)
    .collect(Collectors.toList());

Collecting to a Map

Converts a list to a map keyed by ID for fast lookup.

Map<Long, UmsPermission> permissionMap = permissionList.stream()
    .collect(Collectors.toMap(UmsPermission::getId, p -> p));

Application: Building a Permission Tree

Often we need to return hierarchical data, such as permissions where directories contain menus, which contain buttons. The Stream API makes this easy.

Note: Permissions are linked by pid , where pid is the parent ID and top‑level permissions have pid = 0 .

Define a Node with Children

public class UmsPermissionNode extends UmsPermission {
    private List<UmsPermissionNode> children;

    public List<UmsPermissionNode> getChildren() { return children; }
    public void setChildren(List<UmsPermissionNode> children) { this.children = children; }
}

Method to Get the Tree Structure

public List<UmsPermissionNode> treeList() {
    List<UmsPermission> permissionList = permissionMapper.selectByExample(new UmsPermissionExample());
    List<UmsPermissionNode> result = permissionList.stream()
        .filter(p -> p.getPid().equals(0L))
        .map(p -> covert(p, permissionList))
        .collect(Collectors.toList());
    return result;
}

Recursive Conversion to Add Children

private UmsPermissionNode covert(UmsPermission permission, List<UmsPermission> permissionList) {
    UmsPermissionNode node = new UmsPermissionNode();
    BeanUtils.copyProperties(permission, node);
    List<UmsPermissionNode> children = permissionList.stream()
        .filter(p -> p.getPid().equals(permission.getId()))
        .map(p -> covert(p, permissionList))
        .collect(Collectors.toList());
    node.setChildren(children);
    return node;
}

Project source code: https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-stream

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendjavafunctional programmingStream APIJava 8
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.