Java Program to Implement B+ Tree

Last Updated : 12 Aug, 2026

A B+ Tree is a self-balancing tree data structure commonly used in database indexing and file systems. It stores all actual data in leaf nodes, while internal nodes contain keys that guide searches.

  • All leaf nodes are maintained at the same level.
  • Leaf nodes are linked sequentially, making ordered and range-based traversal efficient.
  • Internal nodes store separator keys that direct searches to the appropriate leaf node.

Organization of the B+ Tree

A B+ Tree consists of internal nodes and leaf nodes:

  • Internal nodes contain keys used to guide searches.
  • Leaf nodes contain the actual data keys.
  • Leaf nodes are linked to support sequential traversal.
  • All leaf nodes remain at the same level, keeping the tree balanced.

For example:

[20, 40]
/ | \
[10, 15] [25, 30] [45, 50]

Here, the root contains separator keys 20 and 40, which guide the search toward the appropriate leaf node. The leaf nodes contain the actual data and can be linked for sequential traversal.

Basic Operations in a B+ Tree

The following operations are implemented in the program:

Operation

Description

Time Complexity

Insertion

Find the appropriate leaf node, insert the data, and manage any required splits

O(log n)

Search

Traverse internal nodes based on keys until reaching the appropriate leaf node, then search within the leaf.

O(log n)

Note: The implementation below focuses on insertion and search operations. It does not implement deletion or deletion-related rebalancing.

Program to Implement B+ Tree

The following Java program implements a simplified B+ Tree with insertion, search, node splitting, and leaf-node linking.

Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

// B+ Tree Node
class BPlusTreeNode {
    boolean isLeaf;

    List<Integer> keys;
    List<BPlusTreeNode> children;

    // Link to the next leaf node
    BPlusTreeNode next;

    public BPlusTreeNode(boolean isLeaf) {
        this.isLeaf = isLeaf;
        this.keys = new ArrayList<>();
        this.children = new ArrayList<>();
        this.next = null;
    }
}

// B+ Tree
class BPlusTree {
    private BPlusTreeNode root;

    // Maximum number of children per internal node
    private final int order;

    public BPlusTree(int order) {
        if (order < 3) {
            throw new IllegalArgumentException(
                "Order must be at least 3"
            );
        }

        root = new BPlusTreeNode(true);
        this.order = order;
    }

    // Find the appropriate leaf node
    private BPlusTreeNode findLeaf(int key) {
        BPlusTreeNode node = root;

        while (!node.isLeaf) {
            int i = 0;

            while (i < node.keys.size()
                    && key >= node.keys.get(i)) {
                i++;
            }

            node = node.children.get(i);
        }

        return node;
    }

    // Insert a key into the B+ Tree
    public void insert(int key) {
        BPlusTreeNode leaf = findLeaf(key);

        insertIntoLeaf(leaf, key);

        // Split leaf if it overflows
        if (leaf.keys.size() > order - 1) {
            splitLeaf(leaf);
        }
    }

    // Insert key into a leaf in sorted order
    private void insertIntoLeaf(
        BPlusTreeNode leaf, int key
    ) {
        int pos = Collections.binarySearch(
            leaf.keys, key
        );

        if (pos < 0) {
            pos = -(pos + 1);
        }

        leaf.keys.add(pos, key);
    }

    // Split a leaf node
    private void splitLeaf(BPlusTreeNode leaf) {
        int mid = (order + 1) / 2;

        BPlusTreeNode newLeaf =
            new BPlusTreeNode(true);

        // Move half the keys to the new leaf
        newLeaf.keys.addAll(
            leaf.keys.subList(
                mid, leaf.keys.size()
            )
        );

        leaf.keys.subList(
            mid, leaf.keys.size()
        ).clear();

        // Maintain linked leaf nodes
        newLeaf.next = leaf.next;
        leaf.next = newLeaf;

        // If root is split, create a new root
        if (leaf == root) {
            BPlusTreeNode newRoot =
                new BPlusTreeNode(false);

            newRoot.keys.add(
                newLeaf.keys.get(0)
            );

            newRoot.children.add(leaf);
            newRoot.children.add(newLeaf);

            root = newRoot;
        } else {
            insertIntoParent(
                leaf,
                newLeaf,
                newLeaf.keys.get(0)
            );
        }
    }

    // Insert a new child into the parent
    private void insertIntoParent(
        BPlusTreeNode left,
        BPlusTreeNode right,
        int key
    ) {
        BPlusTreeNode parent =
            findParent(root, left);

        if (parent == null) {
            throw new RuntimeException(
                "Parent node not found"
            );
        }

        int pos = Collections.binarySearch(
            parent.keys, key
        );

        if (pos < 0) {
            pos = -(pos + 1);
        }

        parent.keys.add(pos, key);
        parent.children.add(pos + 1, right);

        // Split internal node if necessary
        if (parent.keys.size() > order - 1) {
            splitInternal(parent);
        }
    }

    // Split an internal node
    private void splitInternal(
        BPlusTreeNode internal
    ) {
        int mid = (order + 1) / 2;

        int promotedKey = internal.keys.get(mid);

        BPlusTreeNode newInternal =
            new BPlusTreeNode(false);

        // Move keys after the promoted key
        newInternal.keys.addAll(
            internal.keys.subList(
                mid + 1,
                internal.keys.size()
            )
        );

        internal.keys.subList(
            mid,
            internal.keys.size()
        ).clear();

        // Move corresponding children
        newInternal.children.addAll(
            internal.children.subList(
                mid + 1,
                internal.children.size()
            )
        );

        internal.children.subList(
            mid + 1,
            internal.children.size()
        ).clear();

        // If root is split, create a new root
        if (internal == root) {
            BPlusTreeNode newRoot =
                new BPlusTreeNode(false);

            newRoot.keys.add(promotedKey);
            newRoot.children.add(internal);
            newRoot.children.add(newInternal);

            root = newRoot;
        } else {
            insertIntoParent(
                internal,
                newInternal,
                promotedKey
            );
        }
    }

    // Find the parent of a node
    private BPlusTreeNode findParent(
        BPlusTreeNode current,
        BPlusTreeNode target
    ) {
        if (current.isLeaf ||
            current.children.isEmpty()) {
            return null;
        }

        for (BPlusTreeNode child :
             current.children) {

            if (child == target) {
                return current;
            }

            BPlusTreeNode parent =
                findParent(child, target);

            if (parent != null) {
                return parent;
            }
        }

        return null;
    }

    // Search for a key
    public boolean search(int key) {
        BPlusTreeNode leaf = findLeaf(key);

        int pos = Collections.binarySearch(
            leaf.keys, key
        );

        return pos >= 0;
    }

    // Display the tree
    public void printTree() {
        printNode(root, 0);
    }

    private void printNode(
        BPlusTreeNode node,
        int level
    ) {
        System.out.println(
            "Level " + level + ": " + node.keys
        );

        if (!node.isLeaf) {
            for (BPlusTreeNode child :
                 node.children) {
                printNode(child, level + 1);
            }
        }
    }
}

// Main class
public class Main {
    public static void main(String[] args) {

        BPlusTree tree = new BPlusTree(3);

        // Insert keys
        tree.insert(10);
        tree.insert(20);
        tree.insert(30);
        tree.insert(40);
        tree.insert(50);
        tree.insert(60);

        System.out.println(
            "Tree after insertion:"
        );

        tree.printTree();

        // Search for keys
        System.out.println(
            "Search for 30: " +
            tree.search(30)
        );

        System.out.println(
            "Search for 25: " +
            tree.search(25)
        );
    }
} 

Output
Tree after insertion:
Level 0: [30, 50]
Level 1: [10, 20]
Level 1: [30, 40]
Level 1: [50, 60]
Search for 30: true
Search for 25: false

Explanation:

  • Provides a basic structure for B+ Tree with insertion and search operations
  • Handles leaf and internal splits with re-balancing logic
  • Includes a print Tree method to visualize the tree structure

Handling Node Splits

A B+ Tree maintains its balance by splitting nodes when they exceed their allowed capacity.

  • When a leaf node overflows, it is divided into two leaf nodes.
  • The first key of the new leaf is promoted to the parent as a separator.
  • When an internal node overflows, it is also split.
  • If the root overflows, a new root is created.
  • Leaf nodes remain linked after splitting, allowing sequential traversal.

Applications of B+ Tree

B+ Trees are widely used where efficient indexing and ordered data access are required.

  • Database indexing for efficient record retrieval.
  • File systems for managing large amounts of stored data.
  • Range queries because leaf nodes are linked sequentially.
  • Storage systems where balanced search and insertion are important.
Comment