博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 自定义HashSet
阅读量:6591 次
发布时间:2019-06-24

本文共 3608 字,大约阅读时间需要 12 分钟。

前言:

HashSet是一种数据结构为基本操作(add, remove, contains and size)提供恒定的时间性能,假设哈希函数在桶之间正确地分散元素。有许多方法可以实现这种数据结构。这篇文章主要使用链表+数组在Java中简单实现hashmap。

1.定义一个表示链表节点的类

class Node
{ T data; Node next; Node(T data) { this.data = data; this.next = null; } @Override public String toString() { return "(" + this.data + ")"; } }复制代码

2.插入元素

插入元素,键和值将执行以下操作:

  1. 首先计算元素的哈希码,通常是一个int。两个不同的对象可以具有相同的哈希码,因为可能存在无限数量的元素和有限数量的整数。
  2. 然后使用模数作为hashCode (key) % array_length使用哈希码计算数组中的索引。这里两个不同的哈希码可以映射到相同的索引。
  3. 获取上面计算的此索引的链接列表将元素存储在此索引中。由于冲突,使用链表非常重要:可以使用具有相同哈希码的两个不同键或映射到同一索引的两个不同哈希码。

插入实现计算size():

public class MyHashSet
{ private static final Integer INITIAL_CAPACITY = 1 << 4; // 16 private Node
[] buckets; private int size; public MyHashSet(final int capacity) { this.buckets = new Node[capacity]; this.size = 0; } public MyHashSet() { this(INITIAL_CAPACITY); } public void add(T t) { int index = hashCode(t) % buckets.length; Node bucket = buckets[index]; Node
newNode = new Node<>(t); if (bucket == null) { buckets[index] = newNode; size++; return; } while (bucket.next != null) { if (bucket.data.equals(t)) { return; } bucket = bucket.next; } // 仅在最后一个元素没有添加值时才添加 if (!bucket.data.equals(t)) { bucket.next = newNode; size++; } } public int size() { return size; }@Override public String toString() { final StringBuilder sb = new StringBuilder("["); for (Node node: buckets) { while (node != null) { sb.append(node); node = node.next; } } sb.append("]"); return sb.toString(); } private int hashCode(T t) { return t == null ? 0 : t.hashCode(); } }复制代码

3.删除元素

使用以下步骤从hashSet中删除元素:

  1. 计算元素中的哈希码,然后使用模运算从哈希码计算索引。
  2. 获取上面计算的索引的链接列表,并在链接列表中搜索具有此值的值。
  3. 通过更改上一个元素的下一个引用来删除节点。
public T remove(T t) {    int index = hashCode(t) % buckets.length;    Node bucket = buckets[index];    if (bucket == null) {        throw new NoSuchElementException("No Element Found"); //匹配不上    }    if (bucket.data.equals(t)) {        buckets[index] = bucket.next;        size--;        return t;    }    Node prev = bucket;    while (bucket != null) {        if (bucket.data.equals(t)) {            prev.next = bucket.next;            size--;            return t;        }        prev = bucket;        bucket = bucket.next;    }    return null;}复制代码

4.测试

@Testpublic void testMyHashSet() {    MyHashSet
set = new MyHashSet<>(); set.add(3); set.add(4); set.add(8); set.add(4); set.add(8); set.add(1000); assertEquals(4, set.size()); assertEquals(Integer.valueOf(8), set.remove(8)); assertEquals(3, set.size());} @Test public void testMyHashSet1() { MyHashSet
set = new MyHashSet<>(); set.add("USA"); set.add("Nepal"); set.add("England"); set.add("Netherland"); set.add("Canada"); set.add("Bhutan"); assertEquals(6, set.size()); // test removal of element assertEquals("Bhutan", set.remove("Bhutan")); assertEquals(5, set.size()); }复制代码

5.总结

1. 时间复杂

将不同的Keys映射到同一索引可能会发生冲突。如果冲突的数量非常高,最坏情况的运行时间为O(N),其中N是Keys的数量。但是我们通常假设一个良好的实现,将冲突保持在最小,查找时间为O(1)。

2.以上就是关于如何使用基于数组的链表实现hashSet.

转载于:https://juejin.im/post/5cf72c3ef265da1b6b1cc417

你可能感兴趣的文章
Python去掉字符串中空格的方法
查看>>
[转] 用GDB调试程序(五)
查看>>
OCM_第十一天课程:Section5 —》数据仓库
查看>>
水晶报表
查看>>
[转载]测试程序执行时间
查看>>
[转载]回调函数
查看>>
kettle-多文件合并
查看>>
GitHub for Windows一般操作
查看>>
MyEclipse6.5的反编译插件的安装
查看>>
Jenkins + Ansible + Gitlab之ansible篇
查看>>
cogs 539. 牛棚的灯
查看>>
SQL SERVER 备份数据库到指定路径语句
查看>>
3.Knockout.Js(属性绑定)
查看>>
v140平台工具集与v110工具集选择
查看>>
EF6+Sqlite连接字符串的动态设置
查看>>
下拉加载更多
查看>>
您是哪一种类型的老板?
查看>>
SQL SERVER 2012 只能识别20个CPU的问题
查看>>
设计模式(十)外观模式
查看>>
C/C++语言中Static的作用详述
查看>>