数据结构之链表下的Map
Map是一个以键值对存储的接口。Map下有两个具体的实现,分别是HashMap和HashTable.
- Hashtable:底层是数组+链表,线程安全,效率低。不允许null键和null值
- HashMap:底层是数组+红黑树,线程不安全,效率高。允许null键和null值
自定义Map接口
package com.company.map;
/**
* @Author: wenhua
* @CreateTime: 2023-01-10 19:56
*/
public interface Map<K, V> {
// 获取元素个数
int getSize();
// 判断是否包含该元素
boolean isContain(K key);
// 判断map中元素是否为空
boolean isEmpty();
// 添加元素
void add(K key, V value);
// 通过键移除元素并返回元素
V remove(K key);
// 更新元素的键值对
void set(K key, V value);
// 通过键获取对应的值
V get(K key);
}
自定义链表
package com.company.map;
/**
* @Author: wenhua
* @CreateTime: 2023-01-05 20:23
*/
public class Link<K extends Comparable, V> {
/**
* 键值对结点对象
*
* @param <K>
* @param <V>
*/
private class Node<K, V> {
K key;
V value;
Node next;
/**
* 无参构造函数
*/
public Node() {
this(null, null);
}
/**
* 有参构造函数
*
* @param key
* @param value
*/
public Node(K key, V value) {
this.key = key;
this.value = value;
this.next = null;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
", next=" + next +
'}';
}
}
private Node<K, V> head;
private int size = 0;
/**
* 无参构造函数
*/
public Link() {
}
/**
* 有参构造函数
*
* @param key 节点数据
*/
public Link(K key, V value) {
head = new Node(key, value);
}
/**
* 获取链表长度
*
* @return
*/
public int getSize() {
return size;
}
/**
* 判断链表是否为空
*
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 添加链表元素
* 特点:在链表的头部添加
*
* @param key
* @param value
*/
public void add(K key, V value) {
if (isContain(key)) {
set(key, value);
return;
}
// 创建新节点
Node newNode = new Node(key, value);
// 将头结点数据连接在新节点后
newNode.next = head;
// 将新节点连接在头结点的下一个结点
head = newNode;
// 元素个数加1
size++;
}
/**
* 判断链表中是否存在该元素
*
* @param key
* @return
*/
public boolean isContain(K key) {
if (isEmpty())
return false;
Node node = head;
for (int i = 0; i < getSize(); i++) {
if (node.key.equals(key))
return true;
node = node.next;
}
return false;
}
/**
* @param key
* @return
*/
public V get(K key) {
// 判断键是否存在,存在则进行更改,否则返回空
if (isContain(key)) {
Node temp = head;
for (int i = 0; i < getSize(); i++) {
if (temp.key.equals(key))
return (V) temp.value;
temp = temp.next;
}
}
return null;
}
/**
* 更新操作
*
* @param key
* @param value
* @return
*/
public boolean set(K key, V value) {
// 获取需要更改的键值对结点对象
Node node = getNode(key);
// 如果结点不为空,则覆盖值即可
if (node != null) {
node.value = value;
return true;
}
return false;
}
/**
* 通过键获取指定的结点
*
* @param key
* @return
*/
private Node getNode(K key) {
// 判断键是否存在,是则进行查找,否则返回空
if (isContain(key)) {
Node temp = head;
for (int i = 0; i < getSize(); i++) {
if (temp.key.equals(key))
return temp;
temp = temp.next;
}
}
return null;
}
/**
* 移除指定元素
*
* @return
*/
public V remove(K key) {
V value = null;
if (!isContain(key)) {
System.out.println(key + "该元素不存在");
return value;
}
if (head.key.equals(key)) {// 头结点元素为指定元素时
value = head.value;
// 将头结点的下一个结点赋值给头结点
head = head.next;
// 树的数据量减1
size--;
// 删除成功,返回
return value;
} else {// 其他结点为将要删除元素
Node pre = head;
Node temp = head.next;
// for循环遍历,查找将要删除的元素位置
for (int i = 0; i < getSize(); i++) {
if (temp.key.equals(key)) {
value = (V) temp.value;
pre.next = temp.next;
size--;
return value;
}
pre = pre.next;
temp = temp.next;
}
}
return value;
}
@Override
public String toString() {
// 方式以:通过字符串拼接每个节点数据
StringBuffer sbf = new StringBuffer();
Node temp = head;
for (int i = 0; i < getSize(); i++) {
sbf.append(temp.key + ":" + temp.value);
if (temp.next != null) {
temp = temp.next;
sbf.append(",");
}
}
return sbf.toString();
}
}
自定义链表实现Map接口
package com.company.map;
/**
* @Author: wenhua
* @CreateTime: 2023-01-10 09:46
*/
public class MyLinkMap<K extends Comparable,V> implements Map<K,V> {
private MyLink<K,V> myLink;
public MyLinkMap(){
myLink = new MyLink<K,V>();
}
@Override
public boolean isEmpty() {
return myLink.isEmpty();
}
@Override
public boolean isContain(K key) {
return myLink.isContain(key);
}
@Override
public void add(K key,V value) {
myLink.add(key,value);
}
@Override
public V remove(K key) {
return myLink.remove(key);
}
@Override
public void set(K key, V value) {
myLink.set(key,value);
}
@Override
public V get(K key) {
return (V) myLink.getByKey(key);
}
@Override
public int getSize() {
return myLink.getSize();
}
@Override
public String toString() {
return "MyLinkSet{" +
"myLink=" + myLink +
'}';
}
}
Map实现类
package com.company.map;
/**
* @Author: wenhua
* @CreateTime: 2023-01-10 09:46
*/
public class LinkMap<K extends Comparable, V> implements Map<K, V> {
private Link<K, V> link;
public LinkMap() {
link = new Link<K, V>();
}
@Override
public boolean isEmpty() {
return link.isEmpty();
}
@Override
public boolean isContain(K key) {
return link.isContain(key);
}
@Override
public void add(K key, V value) {
link.add(key, value);
}
@Override
public V remove(K key) {
return link.remove(key);
}
@Override
public void set(K key, V value) {
link.set(key, value);
}
@Override
public V get(K key) {
return link.get(key);
}
@Override
public int getSize() {
return link.getSize();
}
@Override
public String toString() {
return "LinkMap{" +
"link=" + link +
'}';
}
}
测试
package com.company.map;
/**
* @Author: wenhua
* @CreateTime: 2023-01-10 21:57
*/
public class Main {
public static void main(String[] args) {
LinkMap<String,Integer> myMap = new LinkMap<String,Integer>();
System.out.println("判断map对象是否为空:"+myMap.isEmpty());
myMap.add("wenhua",23);
System.out.println("判断map对象是否为空:"+myMap.isEmpty());
System.out.println("获取map对象中元素个数:"+myMap.getSize());
System.out.println(myMap.toString());
System.out.println("通过键wenhua获取map对象中对应的元素:"+myMap.get("wenhua"));
myMap.set("wenhua",24);
System.out.println(myMap.toString());
myMap.add("Jim",23);
myMap.add("wenhua",23);
System.out.println(myMap.toString());
System.out.println("通过键wenhua移除并返回map对象中对应的元素:"+myMap.remove("wenhua"));
System.out.println(myMap.toString());
}
}
测试结果
判断map对象是否为空:true
判断map对象是否为空:false
获取map对象中元素个数:1
LinkMap{link=wenhua:23}
通过键wenhua获取map对象中对应的元素:23
LinkMap{link=wenhua:24}
LinkMap{link=Jim:23,wenhua:23}
通过键wenhua移除并返回map对象中对应的元素:23
LinkMap{link=Jim:23}