HashSet的基本概念:
- 元素不能重复
- 不能保证顺序
💓为了实现简单,规定只能存储String,不能为Null,并且区分大小写。
实现思路:
底层用一个数组做为桶,然后每个桶都绑定一个链表;添加的每个元素都先计算其hash值,并对数组的长度取模,然后得到桶的索引,再利用equal判断是否需要添加到链表中即可。
缺陷:
- java中的hashSet链表的长度达到某个值时,会自动转换为红黑树存储(目前没有实现)。
- 目前没有实现扩容,后续实现Redis里面的字典(或者说是map)时,会对扩容的部分进行实现。
具体实现要求:
-
底层提供一个大小固定的容器 buf
-
计算hashCode,并对hashCode取模,从而获取索引
-
解决索引冲突(用链表存储索引相同的元素)
-
扩容策略(暂时先不实现,后面实现Redis中的字典时,再用java实现Redis中字典的扩容策略)
-
保存size
-
遍历操作
-
查询操作
-
删除操作
-
添加操作
Java实现代码:
package basic.hashset;
public class StringHashSet {
private int capacity ;
private int size;
private Node [] buf;
public StringHashSet(){
this.capacity = 10;
this.size = 0;
this.buf = new Node[capacity];
}
public StringHashSet(int capacity){
this.capacity = capacity;
this.size = 0;
this.buf = new Node[capacity];
}
private static class Node{
String val;
Node next;
public Node(String var){
this.val = var;
this.next = null;
}
}
public boolean add(String val){
int index = val.hashCode()%this.capacity;
Node node = new Node(val);
if(buf[index] == null){
buf[index] = node;
}else {//使用链表存储
if(isContain(buf[index],val))
return false;
addOnLast(buf[index], node);
}
size++;
return true;
}
//遍历操作
public void printAll(){
System.out.println("========begin print==========");
for(int i=0;i<buf.length;i++){
System.out.println("index "+i+" : ");
print(buf[i]);
}
}
//打印链表
private void print(Node node){
while (node != null){
System.out.print(node.val+"->");
node = node.next;
}
System.out.println();
}
//判断值是否相同
private boolean isContain(Node node,String val){
while (node != null){
if(node.val.equals(val))
return true;
else
node = node.next;
}
return false;
}
//将node添加到root的尾部节点
private void addOnLast(Node root, Node node){
while (root.next != null){
root = root.next;
}
root.next = node;
}
public boolean contains(String val){
for(int i=0;i<buf.length;i++){
if(isContain(buf[i],val))
return true;
}
return false;
}
public void addAll(Iterable<String> list){
list.forEach( val ->{
add(val);
});
}
public int size(){
return this.size;
}
public boolean remove(String val){
//如果不包含,则直接返回fasle
if(!contains(val))
return false;
for(int i=0;i<buf.length;i++){
if(buf[i] != null){
Node node = removeNode(buf[i], val);
if(node != null){
buf[i] = node;
}
}
}
size--;
return true;
}
private Node removeNode(Node root,String val){
Node begin = root;
if(root.val.equals(val)){
root = root.next;
return root;
}else {
while (root.next != null){
Node pro = root;
root = root.next;
if(root.val.equals(val)){
if(root.next != null){
Node temp = root.next;
pro.next = temp;
}else {
pro.next = null;
}
return begin;
}
}
}
return null;
}
}
测试代码:
public static void main(String[] args) {
StringHashSet stringHashSet = new StringHashSet(3);
boolean add = stringHashSet.add("hello");
System.out.println(add);
stringHashSet.printAll();
boolean add2 = stringHashSet.add("hello");
System.out.println(add2);
stringHashSet.printAll();
boolean add3 = stringHashSet.add("fuck");
System.out.println(add3);
stringHashSet.printAll();
//测试查找
System.out.println(stringHashSet.contains("hello"));
//测试批量插入
List<String> list = new LinkedList<>();
Collections.addAll(list,"a","b","a","b","c","d","e","ggg","abc");
stringHashSet.addAll(list);
stringHashSet.printAll();
//测试容量
int size = stringHashSet.size();
System.out.println(size);
//测试删除
System.out.println(stringHashSet.remove("fuck"));
stringHashSet.printAll();
System.out.println(stringHashSet.size());
System.out.println(stringHashSet.remove("xxx"));
stringHashSet.printAll();
System.out.println(stringHashSet.size());
}
测试结果:
true
========begin print==========
index 0 :
index 1 :
hello->
index 2 :
false
========begin print==========
index 0 :
index 1 :
hello->
index 2 :
true
========begin print==========
index 0 :
index 1 :
hello->
index 2 :
fuck->
true
========begin print==========
index 0 :
c->ggg->abc->
index 1 :
hello->a->d->
index 2 :
fuck->b->e->
9
true
========begin print==========
index 0 :
c->ggg->abc->
index 1 :
hello->a->d->
index 2 :
b->e->
8
false
========begin print==========
index 0 :
c->ggg->abc->
index 1 :
hello->a->d->
index 2 :
b->e->
8
Process finished with exit code 0