Java 两张地图的区别

Java 两张地图的区别,java,algorithm,clojure,hashmap,Java,Algorithm,Clojure,Hashmap,我需要非常有效地比较Clojure/Java中的两个映射,并返回由Java的.equals(..)确定的差异,其中nil/null相当于“notpresent” i、 e.我正在寻找编写函数的最有效方法,如: (map-difference {:a 1, :b nil, :c 2, :d 3} {:a 1, :b "Hidden", :c 3, :e 5}) => {:b nil, :c 2, :d 3, :e nil} 我更喜欢一个不可变的Clojure映射作为输出,但是如果

我需要非常有效地比较Clojure/Java中的两个映射,并返回由Java的.equals(..)确定的差异,其中nil/null相当于“notpresent”

i、 e.我正在寻找编写函数的最有效方法,如:

(map-difference
  {:a 1, :b nil, :c 2, :d 3}
  {:a 1, :b "Hidden", :c 3, :e 5})

=> {:b nil, :c 2, :d 3, :e nil}
我更喜欢一个不可变的Clojure映射作为输出,但是如果性能得到显著改善,Java映射也可以

值得一提的是,我的基本测试用例/行为预期是,对于任意两个映射a和b,以下内容将是相等的(直到null=“Not present”的等价性):

a 
(merge b (difference a b))
实现这一点的最佳方式是什么

  • Clojure地图会很好,因为阅读Clojure地图非常快

  • 我不能回答你,但我可以给你一些东西看。Brenton Ashworth制作了一个测试工具,他用地图比较解决了这个问题。也许你可以看看他的代码,得到实现的提示。 及

  • 我不认为有更好的实现可以比较键并查找键是否不同


  • 在Java中,Google Commons Collections提供了一个非常好的解决方案。

    使用内置的Collections API:

    Set<Map.Entry<K,V>> difference = a.entrySet().removeAll(b.entrySet());
    
    Set difference=a.entrySet().removeAll(b.entrySet());
    
    如果需要将其转换回映射,则必须进行迭代。在这种情况下,我建议:

    Map<K,V> result = new HashMap<K,V>(Math.max(a.size()), b.size()));
    Set<Map.Entry<K,V>> filter = b.entrySet();
    for( Map.Entry<K,V> entry : a.entrySet ) {
        if( !filter.contains( entry ) {
            result.put(entry.getKey(), entry.getValue());
        }
    }
    
    Map result=newhashmap(Math.max(a.size()),b.size());
    Set filter=b.entrySet();
    for(Map.Entry:a.entrySet){
    如果(!filter.contains)(条目){
    put(entry.getKey(),entry.getValue());
    }
    }
    
    我不确定最有效的方法是什么,但以下几点可能很有用:

  • 问题文本中的基本行为预期是不可能的:如果
    a
    b
    是映射,使得
    b
    至少包含一个
    a
    中不存在的键,
    (合并b)
    不能等于
    a

  • 如果最终使用互操作解决方案,但在某个时候需要返回到
    PersistentHashMap
    ,那么

    (clojure.lang.PersistentHashMap/create
     (doto (java.util.HashMap.)
       (.put :foo 1)
       (.put :bar 2)))
    ; => {:foo 1 :bar 2}
    
  • 如果需要将Clojure映射的键集传递给Java方法,可以使用

    (.keySet {:foo 1 :bar 2})
    ; => #< [:foo, :bar]>
    
    我认为在大多数情况下,仅仅做
    (concat(key m1)(key m2))
    并可能复制一些工作可能比在每一步检查“其他映射”中的给定键更有效

  • 总结一下答案,这里有一个非常简单的基于集合的版本,它有一个很好的特性,它说明了它的功能——如果我误解了规范,它应该在这里很明显。:-)


    我不确定它的性能

    (defn map-difference
      [orig other]
      (let [changed (set/difference (set orig) (set other))
            added (set/difference (set (keys other)) (set (keys orig)))]
        (reduce (fn [acc key]
                  (assoc acc key :missing))
          (into {} changed)
          added)))
    
    我使用了
    :missing
    键来避免原始地图中的
    nil
    值与缺失键之间的歧义——当然,您可以将其更改为
    nil
    ,,您也可以使用谷歌番石榴库中的方法,

    那么

    (defn map-diff [m1 m2]
      ;; m1: hashmap
      ;; m2: hashmap
      ;; => the difference between them
      (reduce merge
              (map #(hash-map % (- (or (% m1) 0) (or (% m2) 0)))
                   (keys (merge m1 m2)))))
    

    谢谢这是有用的,尽管比我要找的更一般(它产生了一整套地图比较,而不是我要找的简单差异地图),请修正第三点的语法;这没有道理。回答得太好了,迈克尔,非常感谢!关于1)只有一点,如果您指定nil相当于不存在(根据问题),我认为可以通过将map difference指定nil给需要“删除”的键来满足要求。希望您知道def记录?如果您不需要tha地图是通用的,那么这可能是一个解决方案。@mikera:谢谢,很高兴听到这个消息。:-)对于1),这是一个很好的观点,对任何解决方案来说都应该是一个小小的调整——感谢您的更正@尼克:我不知道你在想什么。你能详细说明一下吗?@Michal Marczyk-你的第一个优雅的基线解决方案发生了什么?我想看一看,但它现在不见了。@Adam Schmideg:我发现它与规格不符,于是把它删掉了。你可以在这个答案的修订历史中看到它(链接到“编辑的某某某某时间前”通知的“某某某某时间前”部分,就在答案文本下面)。老故事,但我想知道clojure 1.3中的
    clojure.data.diff
    对你的问题会有什么影响?
    (defn map-difference
      [orig other]
      (let [changed (set/difference (set orig) (set other))
            added (set/difference (set (keys other)) (set (keys orig)))]
        (reduce (fn [acc key]
                  (assoc acc key :missing))
          (into {} changed)
          added)))
    
    (defn map-diff [m1 m2]
      ;; m1: hashmap
      ;; m2: hashmap
      ;; => the difference between them
      (reduce merge
              (map #(hash-map % (- (or (% m1) 0) (or (% m2) 0)))
                   (keys (merge m1 m2)))))