哈希表是要走的路。
它们的查找、插入和删除平均复杂度为 O(1)!
它们往往比树占用更多的内存,但速度要快得多。
由于您只是使用 32 位整数(您当然可以将 IP 转换为 32 位整数),因此事情会非常简单和快速。
您可以只使用排序数组。插入和删除成本是 O(n),但查找是 O(log n),尤其是每个 ip 的内存只有 4 个字节。
实现很简单,可能太多了:D
二叉树的查找、插入和删除复杂度为 O(log n)。
一个简单的二叉树是不够的,但是您需要一个 AVL 树或一个红黑树,这可能非常烦人且难以实现。
AVL 和 RBT 树能够自我平衡,我们需要它,因为不平衡的树的查找时间复杂度最差,为 O(n),这与简单的链表相同!
如果你需要禁止 ip 范围而不是单个和唯一的 ip,可能你需要一个 Patricia Trie,也称为 Radix Tree,它们是为单词字典和 ip 字典发明的。
但是,如果写得不好\平衡,这些树可能会变慢。
哈希表总是更适合简单的查找!它们太快了,不真实:)
现在关于同步:
如果您只在应用程序启动时填写一次黑名单,您可以使用一个没有多线程和锁定问题的纯只读哈希表(或基数树)。
如果你不需要经常更新,我建议你使用读写锁。
如果您需要非常频繁的更新,我建议您使用并发哈希表。
警告:不要自己编写,它们非常复杂且容易出错,请在网络上找到实现!
他们大量使用新处理器的(相对)新的原子 CAS 操作(CAS 表示比较和交换)。这些是一组特殊的指令或指令序列,允许在单个原子操作中比较和交换内存上的 32 位或 64 位字段,而无需锁定。
使用它们可能很复杂,因为您必须非常了解您的处理器、操作系统、编译器和算法本身是违反直觉的。
有关 CAS 的更多信息,请参阅http://en.wikipedia.org/wiki/Compare-and-swap。
并发AVL树被发明了,但它太复杂了,我真的不知道该说些什么:)例如http://hal.inria.fr/docs/00/07/39/31/PDF/RR-2761.pdf
我刚刚发现并发基数树存在:
ftp://82.96.64.7/pub/linux/kernel/people/npiggin/patches/lockless/2.6.16-rc5/radix-intro.pdf 不过也挺复杂的。
并发排序数组当然不存在,更新需要读写锁。
还要考虑处理非并发哈希表所需的内存量可能非常少:对于每个 IP,您需要 4 个字节的 IP 和一个指针。
您还需要一个大的指针数组(或带有一些技巧的 32 位整数),其大小应该是大于应该存储的项目数的素数。
如果您愿意,哈希表当然也可以在需要时自行调整大小,但它们也可以存储比素数更多的项目,但代价是查找时间变慢。
对于树和哈希表,空间复杂度都是线性的。
我希望这是一个多线程应用程序而不是多进程应用程序(fork)。
如果不是多线程,则无法以快速可靠的方式共享部分内存。