Tree set
A hash set answers "is it here?" and nothing about order. A TreeSet<T> answers the same question and also keeps its elements sorted, the way a tree map keeps its keys.
That makes it the set to reach for when the members should come out in order — a word list in alphabetical order, free seat numbers lowest first — or when the question is "which member comes next after this one?".
Making a tree set
Like a tree map, it is told how to order two elements:
var tags = TreeSet<char8[..]>(allocator, CompareSlice);
CompareSlice orders text by its bytes. For lowercase English words that is alphabetical order; the Common mistakes below show where it is not.
Membership, as in a hash set
Insert reports whether the element was new, and a duplicate is not added a second time:
let incoming: char8[..][7] = ["rust", "memory", "arena", "rux", "memory", "pool", "arena"];
for tag in incoming {
let isNew = tags.Insert(tag)?;
if !isNew {
PrintLine("duplicate {}", tag);
}
}
Seven tags go in; memory and arena arrive twice, so five are kept. Contains asks without changing anything, just as on a hash set.
Walking in order
A for loop walks the set in order, whatever order the elements arrived in:
for tag in tags {
Print(" {}", tag);
}
They arrived as rust memory arena rux pool; they come out as arena memory pool rust rux.
Ordered questions
The same questions a tree map answers about its keys, a tree set answers about its elements. Each returns an optional, because the answer may not exist — so the program supplies a stand-in with ??:
PrintLine("first {}", tags.First() ?? "(none)");
PrintLine("last {}", tags.Last() ?? "(none)");
PrintLine("from n on {}", tags.Ceiling("n") ?? "(none)");
PrintLine("from z on {}", tags.Ceiling("z") ?? "(none)");
Ceiling is the smallest element at or above the one asked for. "n" is not a tag, so Ceiling("n") finds the first tag from n on: pool. Nothing comes at or after "z", so that one prints (none).
flowchart LR
a["arena"] --> m["memory"] --> p["pool"] --> r["rust"] --> x["rux"]
n(["Ceiling of n"]) -. "smallest element<br/>at or above n" .-> p
f(["Floor of n"]) -. "largest element<br/>at or below n" .-> m| Method | Returns |
|---|---|
First() | the smallest element |
Last() | the largest element |
Floor(x) | the largest element at or below x |
Ceiling(x) | the smallest element at or above x |
Range(a, b) | a walk from a up to, not including, b |
A range of elements
Range(start, end) walks the elements from start up to, but not including, end:
for tag in tags.Range("r", "s") {
Print(" {}", tag);
}
Every word that starts with r sorts at or after "r" and before "s", so this prints rust rux — a prefix search in one call.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// A hash set answers "is it here?" and nothing about order. A `TreeSet<T>` answers the same
// question and also keeps its elements sorted, the way a tree map keeps its keys.
//
// That makes it the set to reach for when the members should come out in order — a word list
// in alphabetical order, a list of free seat numbers lowest first — or when the question is
// "which member comes next after this one?".
import Allocator::{ Allocator, SystemAllocator };
import Collections::{ CollectionError, CompareSlice, TreeSet };
import Io::{ Print, PrintLine };
func Main() -> ! CollectionError {
var system = SystemAllocator();
let allocator: Allocator = system;
// Like a tree map, it is told how to order two elements. `CompareSlice` orders text by its
// bytes, which is alphabetical for lowercase English words.
var tags = TreeSet<char8[..]>(allocator, CompareSlice);
// Membership works as in a hash set: `Insert` reports whether the element was new, and a
// duplicate is not added a second time.
let incoming: char8[..][7] = ["rust", "memory", "arena", "rux", "memory", "pool", "arena"];
for tag in incoming {
let isNew = tags.Insert(tag)?;
if !isNew {
PrintLine("duplicate {}", tag);
}
}
PrintLine("{} tags in, {} kept", incoming.length, tags.Length());
PrintLine("has pool? {}", tags.Contains("pool"));
// A `for` loop walks the set in order, whatever order the elements arrived in.
Print("in order ");
for tag in tags {
Print(" {}", tag);
}
PrintLine();
// Ordered questions return optionals, because the answer may not exist. `Ceiling` is the
// smallest element at or above the one asked for, so it finds the first tag from "n" on.
PrintLine("first {}", tags.First() ?? "(none)");
PrintLine("last {}", tags.Last() ?? "(none)");
PrintLine("from n on {}", tags.Ceiling("n") ?? "(none)");
PrintLine("from z on {}", tags.Ceiling("z") ?? "(none)");
// `Range(start, end)` walks the elements from `start` up to, but not including, `end`.
Print("r to s ");
for tag in tags.Range("r", "s") {
Print(" {}", tag);
}
PrintLine();
}
Besides Io, its Rux.toml lists Allocator and Collections under [Dependencies].
Run it
cd Examples/Collections/TreeSet
rux run
duplicate memory
duplicate arena
7 tags in, 5 kept
has pool? true
in order arena memory pool rust rux
first arena
last rux
from n on pool
from z on (none)
r to s rust rux
Common mistakes
CompareSlice.It compares bytes, and every uppercase letter's byte is smaller than every lowercase one's. Insert
"Zebra", "apple" and "mango", and the walk gives Zebra apple mango. Store words in one case when you want alphabetical order.First, Last, Floor and Ceiling return optionals, which cannot be printed as they are: PrintLine("{}", tags.First()) fails with error: argument 2 to 'PrintLine' has type 'char8[..]?', but variadic parameter 'args' requires 'Display'. Supply a stand-in with ??, as the program does, or match on the result.Remove to return the element.A hash set's
Remove hands the element back as an optional; a tree set's returns a bool — whether it was there. Write if tags.Remove("pool") { … }.Try it yourself
- Print
tags.Floor("n"), the largest tag at or below"n". ThenFloor("a")— what does it give, and why? - Insert
"Zebra"and see where it lands in the walk. Then insert it as"zebra". - Keep free seat numbers in a
TreeSet<int32>made withCompareInt32. Take the lowest free seat withFirst(), and remove it so it is no longer free. - Print every tag from
"a"up to, but not including,"n"withRange.
Learn more
17.6 Tree map
Insert keys into a TreeMap<K, V> out of order, then walk them in order and ask for a range.
Overview
Sorting, searching and folding slices with the Algorithms package: five lessons on sorting in place, linear and binary search, the smallest and largest element, and collapsing a slice into one answer.