下午好,
对于我目前的计算机科学课程,我们的教授让我们实现一个HashMap类,而不使用Java内置的Map类(除了使用它的接口)。我已经完成了大部分工作,现在处于调试模式。我的教授评级服务器告诉我,我总是返回一个“大小”比预期的少,即。当我预期49号时,我的班级将返回48号。我想我已经把它缩小到put()方法,因为它是唯一一个大小向上递增的方法,但我不确定。任何有关该课程的信息将不胜感激。另外,作为一个重要的侧面说明,我正在使用“链接”冲突解决技术。
谢谢大家!
{package adt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("unchecked")
public class HashMap<K , V> implements Map<K, V>{
//linked list style
private class Entry<K, V> {
private K key;
private V value;
private Entry<K, V> next;
private Entry( K key, V value){
this.key = key;
this.value = value;
this.next = null;
}
}
private Entry<K, V>[] table = new Entry[1024]; // Creates a table of Entries. Because of the chaining implementation, each Entry will be treated like a linked list, so each Entry has a .next.
int size = 0;
public HashMap(){
for(int i =0; i<table.length; i++){
table[i] = null;
}
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size ==0;
}
@Override
public boolean containsKey(Object key) {
int location = Hash(key) % table.length;
Entry<K, V> e = table[location];
if(table[location] == null){
return false;
}else{
while (e!= null && e.key.equals(key) == false){
e = e.next;
if( e.key.equals(key)){
return true;
}
}
}
return false;
}
@Override
public boolean containsValue(Object value) {
for (int i = 0; i<table.length; i++){
if(table[i] != null){
Entry<K, V> e = table[i];
while( e.value.equals(value)==false){
e = e.next;
if(e.value.equals(value)){
return true;
}
}
}
}
return false;
}
@Override
public V get(Object key) {
V value = null;
int location = Hash(key)% table.length;
if (table[location] == null){
value = null;
}else{
Entry<K, V> e = table[location];
while(e !=null && e.key.equals(key) == false){
e = e.next;
if (e == null){
value = null;
}
else{
value = (V) e.value;
}
}
}
return value;
}
@Override
public V put(K key, V value) {
V returnValue = null;
int location = Hash(key) % table.length;
if ( table[location]== null){
table[location] = new Entry< K, V>(key, value);
size++;
}
else{
Entry<K, V> e = table[location];
while ( e.next != null && e.key.equals(key) == false){
e = e.next;
if( e.key.equals(key)){
e.value = value;
returnValue = e.value;
}else if(e.next == null){
e.next = new Entry<K, V>(key, value);
returnValue = e.value;
size++;
}
}
}
return returnValue;
}
@Override
public V remove(Object key) {
int location = Hash(key) % table.length;
V value = null;
Entry<K, V> e = table[location];
if(table[location] == null){
value = null;
}else {
while(e.next != null && e.key.equals(key) == false){
e = e.next;
if(e.key.equals(key)){
value = e.value;
e = null;
size--;
}else{
value = null;
}
}
}
return value;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
// TODO Auto-generated method stub
}
@Override
public void clear() {
for(int i =0; i<table.length; i++){
table[i] = null;
}
size = 0;
}
@Override
public Set<K> keySet() {
Set<K> s = new HashSet<K>();
Entry<K, V> e;
if(!isEmpty()){
for(int i = 0; i<table.length; i++){
e = table[i];
if(e != null){
s.add(e.key);
while(e.next != null){
s.add(e.key);
e=e.next;
}
}
}
}
return s;
}
@Override
public Collection<V> values() {
Collection<V> c = new ArrayList<V>();
Entry<K,V> e;
if(isEmpty()==false){
for(int i=0; i<table.length;i++){
e = table[i];
if(e != null){
c.add(e.value);
while(e.next !=null){
c.add(e.value);
}
}
}
}
return c;
}
@SuppressWarnings("rawtypes")
@Override
public Set entrySet() {
Set<Entry> s = new HashSet<Entry>();
Entry<K,V> e;
if(isEmpty()==false){
for(int i = 0; i<table.length; i++){
e = table[i];
if( e!=null){
while(e.next != null){
s.add(e);
}
}
}
}
return s;
}
public int Hash(Object k){
String key = k.toString();
int n = 13;
for(int i = 0; i<key.length(); i++){
n += n + key.charAt(i);
}
n = n*31;
return n;
}
public boolean equals(Object o){
Map<K, V> m2;
if( o instanceof Map){
m2 = (Map<K,V>)o;
if(entrySet().equals(m2.entrySet())){
return true;
}
}
return false;
}
}
}
答案 0 :(得分:2)
通常情况下,似乎有几种情况下,e
值预先增加到e.next
,甚至没有比较e.key
。
以put
为例,因为这就是我们所说的。
您正在跳过第一个条目的密钥。
你应该像对待for循环一样对待它。初始化,测试一些条件,然后增量
int location = Hash(key) % table.length;
Entry<K, V> e = table[location];
while ( e.next != null && !e.key.equals(key)) {
if( e.key.equals(key)) {
// do some stuff
}
e = e.next;
}
或者,您可以将其重写为像这样的for循环
int location = Hash(key) % table.length;
for (Entry<K, V> e = table[location]; e.next != null && !e.key.equals(key); e = e.next) {
if( e.key.equals(key)) {
// do some stuff
}
}
几乎代码中的每个方法似乎都会执行相同的预增量操作,这会导致您跳过第一个条目的键。
你所采用的方法的另一个问题是while循环的部分是e.key.equals(key) == false
。
(忽略你可以像上面那样重写的事实)......如果键相等,那么将跳过整个while循环。
我的建议是简单地从while
删除该条件,因为你正在比较while循环中的键。
但具体回到put
。
你的if-else条件并不匹配。你比较的变量是不同的,所以没有&#34; else&#34;条件。您需要两个单独的if语句来处理不同的条件,如此。并且
if(e.next == null){
e.next = new Entry<K, V>(key, value);
size++;
returnValue = e.value;
}
if(e.key.equals(key)){
e.value = value;
returnValue = e.value;
}
答案 1 :(得分:2)
@ cricket_007发现你弄错了,但我想发布我的版本。我认为它更具可读性,逻辑清晰。
Objects.equals
这段代码有两个截然不同的部分:第一部分是搜索现有的键,直到你耗尽所有这些键。只要找到一个匹配作为参数传递的密钥,就可以返回。
第二部分是未找到匹配时发生的情况。在这种情况下,大小总是递增。这使得您在需要或不需要增加大小和插入元素时非常清楚。
加分: null
处理现有密钥为Objects.hashCode
的情况。如果您将其与null
或任何接受null
的哈希结合使用,则您的地图可以支持config/routes.rb
个密钥。