idlerus/sorted-linked-list
Composer 安装命令:
composer require idlerus/sorted-linked-list
包简介
Linked list that keeps its values sorted (int or string).
README 文档
README
A doubly linked list for PHP that keeps its values sorted at all times — so you never have to call sort() again. You insert, it files the value exactly where it belongs. Like a well-trained librarian, just without the disapproving looks.
Holds either ints or strings — never both. The type is locked by the first inserted value, because PHP has no generics and somebody has to keep order around here.
Features
- Always sorted — values are placed at their sorted position on insert; no explicit sorting, ever
- Both reading directions — ascending or descending via
SortOrder::Asc/SortOrder::Desc, powered by backward links (no copying, no reversing) - Configurable duplicate handling —
DuplicatePolicy::Allow(default),Ignore(silently skipped), orReject(throws) - Configurable string comparison — case-sensitive by default, case-insensitive via
StringComparison::CaseInsensitive; one comparison drives ordering and duplicate detection, consistently - Type safety at runtime — mixing ints and strings throws a
TypeMismatchExceptioncarrying both type names - Fast paths where it counts — inserting a new minimum/maximum and rejecting out-of-range lookups are O(1); feeding it already-sorted data (timestamps, IDs) is as fast as it gets
- GC friendly — node links are broken on
clear()/destruction, so memory is freed by plain refcounting without waiting for the cycle collector - Plays nicely with PHP — implements
IteratorAggregate(justforeachit) andCountable(count($list)is O(1))
Requirements
- PHP >= 8.2
Installation
composer require idlerus/sorted-linked-list
Usage
use Idlerus\SortedLinkedList\SortedLinkedList; use Idlerus\SortedLinkedList\SortOrder; use Idlerus\SortedLinkedList\DuplicatePolicy; $list = new SortedLinkedList(); $list->insert(5); $list->insert(1); $list->insert(3); $list->toArray(); // [1, 3, 5] $list->toArray(SortOrder::Desc); // [5, 3, 1] foreach ($list as $value) { // 1, 3, 5 — sorted, always } $list->contains(3); // true $list->min(); // 1 $list->max(); // 5 count($list); // 3 $list->remove(3); // true — removes one occurrence $list->clear(); // empty list, type lock released
Duplicates, your way
// Allow (default): duplicates sit next to each other $list = new SortedLinkedList(); // Ignore: duplicates are skipped, insert() tells you what happened $list = new SortedLinkedList(DuplicatePolicy::Ignore); $list->insert(5); // true $list->insert(5); // false — already there, nothing changed // Reject: duplicates are an error $list = new SortedLinkedList(DuplicatePolicy::Reject); $list->insert(5); $list->insert(5); // throws DuplicateValueException ($e->value === 5)
Case sensitivity
By default, string comparison is byte-wise — which means case-sensitive, and ASCII ordering applies ('Cherry' < 'apple', since uppercase sorts first). If that surprises your users, flip the switch:
use Idlerus\SortedLinkedList\StringComparison; $list = new SortedLinkedList(comparison: StringComparison::CaseInsensitive); $list->insert('banana'); $list->insert('Cherry'); $list->insert('apple'); $list->toArray(); // ['apple', 'banana', 'Cherry'] — dictionary-like $list->contains('APPLE'); // true // Combines with duplicate policies — equality follows the same comparison: $list = new SortedLinkedList(DuplicatePolicy::Ignore, StringComparison::CaseInsensitive); $list->insert('Apple'); // true $list->insert('apple'); // false — same word, different costume
Note: locale-aware collation (accents, non-ASCII alphabets) is out of scope; for that, reach for the intl extension's Collator.
One type per list
$list = new SortedLinkedList(); $list->insert(1); $list->insert('one'); // throws TypeMismatchException // ($e->expectedType === 'int', $e->actualType === 'string')
API overview
| Method | Description | Complexity |
|---|---|---|
insert(int|string $value): bool |
Inserts at sorted position | O(n), O(1) for new min/max |
remove(int|string $value): bool |
Removes first occurrence | O(n), O(1) out of range |
contains(int|string $value): bool |
Membership check | O(n), O(1) out of range |
min() / max() |
Smallest / largest value or null | O(1) |
count() / isEmpty() |
Size queries | O(1) |
toArray(SortOrder $order) |
All values as array | O(n) |
iterate(SortOrder $order) |
Lazy generator, either direction | O(n) |
clear() |
Empties the list, unlocks the type | O(n) |
Development
Clone, then:
composer install composer test # run the test suite composer cs # check coding standard composer cs-fix # auto-fix what can be fixed composer check # cs + tests in one go
Dev dependencies & what they guard
| Package | Role |
|---|---|
phpunit/phpunit |
Test framework. Runs the suite — unit tests for every branch (head/middle/tail, all policies) plus randomized tests that replay thousands of mutations against a plain-array reference implementation. |
squizlabs/php_codesniffer |
Coding-standard enforcement (phpcs) and auto-fixing (phpcbf). The ruleset lives in phpcs.xml.dist: PSR-12 as the base, plus required PHPDoc on every method. |
slevomat/coding-standard |
Extra sniffs on top of PSR-12: strict types declarations, type-hint completeness, import hygiene (no fallback globals), dead-code detection, early-exit style. The picky friend every codebase needs. |
The dealerdirect/phpcodesniffer-composer-installer plugin (pulled in automatically) registers the Slevomat standard with PHP_CodeSniffer, so phpcs finds it without any manual path configuration.
Design notes
- Doubly linked — each node holds
prevandnext. That is what makes descending iteration and O(1)max()possible; the price is one extra pointer per node. - Values are stored ascending, direction is a read concern — one list serves both directions at once, no per-instance ordering lock.
- Sorted order is exploited everywhere — lookups stop as soon as values pass the target, and anything outside the min/max range is answered in O(1) without touching the list.
- Known limitation — insertion into the middle is O(n); a linked list cannot binary-search. If you need O(log n) inserts, you are shopping for a skip list, not a linked list.
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-12