Splitwise Machine Coding in Java: Splits and Settle Up
Code a Splitwise-style expense sharing app in Java: money in paise, equal, exact and percent splits as strategies, net balances and a greedy settle-up.
- Course: Low-Level Design study plan
- Module: Expense sharing
- Kind: Lesson
- Reading time: 14 min
- Runtime: OpenJDK 18
How do you code an expense sharing app like Splitwise in a machine-coding round?
Keep money in integer paise, one net balance per member, and each split rule (equal, exact, percent) as a strategy that turns an amount into shares and validates its own input. An expense adds the amount to the payer and subtracts each share. Settling repeatedly has the largest debtor pay the largest creditor until every balance is zero.
Lesson
An expense app is a ledger with one invariant: money is moved, never created, so a group's balances always add up to zero. The round tests the split rules, the money arithmetic and the settle-up. The expense sharing note has the full design; this lesson is the code.
The classes
- Member: a name. BalanceSheet holds one net balance per member: positive means the group owes them.
- Split (interface):
int[] shares(long amount, …), one class per rule:EqualSplit,ExactSplit,PercentSplit. Each validates its own input before any balance moves. This is the Strategy pattern. - Expense: payer, amount, split. Applying it: payer
+amount, every share-share, the payer's own share included.
Money in paise
12.50is read as1250, along. Never adouble: 0.1 has no exact binary form and the sums drift.- An equal split of 100.00 among three is 33.34, 33.33, 33.33: the leftover paisa goes to the first member.
- A percent split floors each share; the leftover goes to the first member.
- Print with two decimals at the very end, and only there.
Settling up
- Find the member owed the most and the member who owes the most (ties by name, so the output is fixed).
- The debtor pays the smaller of the two amounts; at least one reaches zero.
- Repeat until every balance is zero: at most n - 1 payments.
asha ben chen dev · EXPENSE asha 1200 EQUAL … · EXPENSE ben 200 EQUAL chen dev · SETTLE- Four friends, every balance at zero. A positive balance will mean the group owes that member; a negative one, that the member owes.
- asha pays 1200.00 for all four. She gets +1200.00 and everyone, asha included, gets -300.00: asha ends at +900.00, the others at -300.00.
- ben pays 200.00 for chen and dev. He moves up by 200.00 and they move down by 100.00 each. The bars still add up to zero: money moved, none was made.
- Settle: the largest creditor is asha (+900.00), the largest debtors chen and dev tie at -400.00, and the earlier name goes first. chen pays the smaller amount, 400.00.
- asha is still owed 500.00; dev owes the most, 400.00, and pays it all. dev is now square.
- Only ben owes now, 100.00, exactly what asha is still owed. One payment settles both: three transfers for four people, within the n - 1 bound.
Greedy is not always the true minimum (that problem is NP-hard), but it is short, fast and never needs more than n - 1 transfers.
The traps
- Pairwise debts. Tracking "A owes B" for every pair is fine for display, but settling needs only the net balances.
- Validation after the update. An exact split that does not add up must be refused before any balance changes, or the ledger is left half-applied.
- Rounding by accident.
amount / ndrops paise; hand them out on purpose.
Common questions
Why store money in paise instead of a double?
A double cannot hold 0.1 exactly, so sums of rupee amounts drift by fractions of a paisa and balances stop adding up to zero. A long of paise is exact; parse "12.50" into 1250 and format it back only when printing.
Where do the leftover paise go in an equal split?
100 rupees among three is 3333 paise each with 1 paisa left over. Give the leftover units to the first members, one each, so the shares add up to the amount exactly; any fixed rule works as long as nothing is lost.
How does the greedy settle-up work?
Take the member owed the most and the member who owes the most; the debtor pays the smaller of the two amounts, which brings at least one of them to zero. Repeat until all balances are zero. It needs at most n - 1 payments for n members.
Why must a group's balances always add up to zero?
Every expense adds its amount to the payer and subtracts the same amount, split into shares, from the members. Money is moved, never created, so the sum stays zero; asserting it after each expense catches a rounding or split bug at once.
Exercises
Split strategies over paise
Record a group's expenses with three split rules and report the net balances.
Input. The first line lists the members' names. Amounts are rupees with at most two decimals (900, 10.5, 40.25); keep them as paise in a long. Then one command per line:
EXPENSE payer amount EQUAL u1 u2 …: equal shares; leftover paise go one each to the first members listed.EXPENSE payer amount EXACT u1 a1 u2 a2 …: the given amounts, which must add up to the expense, elseexact shares add up to X, not Y.EXPENSE payer amount PERCENT u1 p1 u2 p2 …: whole percentages that must total 100, elsepercents add up to N, not 100. Each share is floored to the paisa; the leftover goes to the first member listed.
Check in this order: the payer is a member, else unknown user NAME; the split type is known, else unknown split TYPE; each participant is a member, else unknown user NAME; then the split's own rule. A refused expense changes nothing. Otherwise the payer gets +amount and each participant -share (the payer may or may not be one of them), and the program prints payer paid 900.00: asha 300.00, ben 300.00, chen 300.00 with the participants in the order given.
BALANCES: every member in name order with their net balance, such asasha -150.00,ben +250.00,chen 0.00.
Example
kim lee max
EXPENSE kim 100 EQUAL kim lee max
BALANCES
prints
kim paid 100.00: kim 33.34, lee 33.33, max 33.33
kim +66.66
lee -33.33
max -33.33Settle up
Settle a group's balances with the greedy rule.
Input. The first line lists the members. Money is paise, as in the first program. Then one command per line:
EXPENSE payer amount EQUAL u1 u2 …: an equal split, exactly as before (onlyEQUALappears here), printingpayer paid 1200.00: asha 300.00, …. An unknown payer or member printsunknown user NAMEand changes nothing.PAY from to amount:fromhandstomoney directly:from's balance goes up by the amount andto's goes down. Printsfrom paid to 10.00, orunknown user NAME(checkingfromfirst).SETTLE: while anyone is owed money, the member who owes the most pays the member owed the most the smaller of the two amounts; ties go to the earlier name. Printdebtor pays creditor 400.00for each payment, thentransfers: N. The payments are applied, so every balance is zero afterwards.BALANCES: every member in name order:asha +150.00,ben -250.00,chen 0.00.
Example
asha ben chen dev
EXPENSE asha 1200 EQUAL asha ben chen dev
EXPENSE ben 200 EQUAL chen dev
SETTLE
prints
asha paid 1200.00: asha 300.00, ben 300.00, chen 300.00, dev 300.00
ben paid 200.00: chen 100.00, dev 100.00
chen pays asha 400.00
dev pays asha 400.00
ben pays asha 100.00
transfers: 3In this module: Expense sharing
← Checkpoint: Movie ticket booking · Checkpoint: Expense sharing →