【问题标题】:how to implement a Map in java correctly?如何在java中正确实现地图?
【发布时间】:2020-07-22 15:14:29
【问题描述】:

所以我正在尝试实现我自己的 Map 以了解它是如何工作的。 我之前通过在我的实现中创建了 2 个私有列表来做到这一点,但我被告知要使用和 Entry 类。所以我现在的主要问题是如何比较存储在列表中的条目的键?这是我的代码

   import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class CorrectMapImpl implements Map {
private List<Entry> entries = new ArrayList<>();

@Override
public Object put(Object key, Object value) {
    Entry entry = new Entry(key, value);
    if (entries.contains(entry)) {
        int index = entries.indexOf(entry);
        entries.set(index, (Entry) value);
    }else if(entries.contains()){

    } else  {
        entries.add(entry);
    };
    return entry.getValue();
}

@Override
public Object get(Object key) {
   //return entries.stream().filter(e -> e.getKey().equals(key)).findFirst().orElse(null).getValue();
    /*Optional<Entry> optionalEntry = entries.stream().filter(e -> e.getKey().equals(key)).findFirst();
    optionalEntry.ifPresent(Entry::getValue);
    return optionalEntry;*/
}

@Override
public int size() {
    return entries.size();
}

@Override
public Object remove(Object key) {
    boolean hasKey = false;
    int keyIndex = 0;
    for(Entry entry: entries) {
        if(entry.getKey().equals(key)) {
            hasKey = true;
            break;
        }
        keyIndex++;
    }
    if(hasKey) {
        return entries.remove(keyIndex).getValue();
    }
    return null;
}
//---------------------------------------------------------------------------------------------
class Entry {
    private Object key;
    private Object value;

    public Entry(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    public Object getKey() {
        return key;
    }

    public void setKey(Object key) {
        this.key = key;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Entry)) return false;
        Entry entry = (Entry) o;
        return Objects.equals(key, entry.key) &&
                Objects.equals(value, entry.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(key, value);
    }
}

} “put”没有完整地写出来。我添加了“else if”,因为没有它“put”没有测试相同键/不同值的条目,只有当条目的实例完全相同时,键和值。当我尝试 entry.put 一个具有相同键但值不同的条目的新实例时,它不会像必须那样覆盖具有相同键的条目的前一个实例,而是添加一个新实例,因此大小为列表递增。 “get”实现也不起作用。第一条语句抛出 NullPointerException,另一条则提示对象的类,如 (CorrectMapImpl$Entry@cd440b02),而不是设置为键的值。

【问题讨论】:

    标签: java implementation


    【解决方案1】:

    现在你有一个Entry 对象的列表。请参阅java API,其中指出:

    [contains] 如果此集合包含指定元素,则返回 true。更正式地说,当且仅当此集合包含至少一个元素 e 满足 (o==null ? e==null : o.equals(e)) 时才返回 true。

    这意味着当你写的时候:

    if (entries.contains(entry)) {
    

    实际上相当于

    for(Entry e : entries) if(e.equals(entry){...}
    

    Entry 类中equals() 的代码,

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Entry)) return false;
        Entry entry = (Entry) o;
        return Objects.equals(key, entry.key) &&
                Objects.equals(value, entry.value);
    }
    

    意味着a.equals(b) 将仅在 both 值相同时返回 true。

    这就是你的代码失败的原因。我认为正确的解决方案不是改变您当前实现 .equals() 的方式,因为您实现它的方式确实定义了两个 Entry 对象的相等性。

    我的建议是将.contains(entry) 代码替换为手动循环列表并手动比较键。像这样的

    for(int i = 0; i < entries.size(); i++){
      if(entries.get(i).getKey().equals(entry.getKey())) {
        index = i;
        break;
      }
    }
    

    【讨论】:

      【解决方案2】:

      您的Entry 类应该实现Map.Entry 接口。

      put 方法应该查找具有给定键的现有条目。您可以使用 for - 循环来做到这一点:

          public Object put(Object key, Object value) {
              Entry foundEntry = null;
              for (Entry entry : entries) {
                  if (key.equals(entry.getKey())) {
                      foundEntry = entry;
                  }
              }
      

      当循环结束时,要么你找到了一个具有相同键的条目,要么没有。

      • 如果找到条目,则必须更新其值。
      • 如果您没有找到条目,则必须创建一个新条目并将其添加到地图中。

      不要忘记您必须返回条目的旧值。

              if (foundEntry == null) {
                  // Key not in map: create new Entry
                  entries.add(new Entry(key, value));
                  return null;
              } else {
                  // Key already in map: update Entry
                  Object oldValue = foundEntry.getValue();
                  foundEntry.setValue(value);
                  return oldValue;
              }
          }
      

      get 方法应该类似于 put 方法的第一部分:首先查找具有右键的条目。如果找到这样的条目,则返回其值。否则,返回null

      其他提示对象的类,如 [CorrectMapImpl$Entry@cd440b02)

      您需要在 Entry 中添加一个toString 方法。见How do I print my Java object without getting "SomeType@2f92e0f4"?

      【讨论】:

      • 是的,我确实喜欢它并且它确实有效,但我想尝试使用流和 lambda,因为这些东西真正缩短了代码,我认为它被称为语法糖。
      • 如果需要,可以用 .stream().filter(..).findFirst() 替换显式循环
      猜你喜欢
      • 1970-01-01
      • 2013-10-03
      • 1970-01-01
      • 2016-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-04
      相关资源
      最近更新 更多