java 两张地图的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3387155/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 01:36:04  来源:igfitidea点击:

Difference between two maps

javaalgorithmclojurehashmap

提问by mikera

I need to very efficiently compare two maps in Clojure/Java, and return the difference as determined by Java's .equals(..), with nil/null equivalent to "not present".

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

i.e. I am looking for the most efficient way to a write a function like:

即我正在寻找编写函数的最有效方法,例如:

(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}

I'd prefer an immutable Clojure map as output, but a Java map would also be fine if the performance improvement would be significant.

我更喜欢使用不可变的 Clojure 映射作为输出,但如果性能改进显着,Java 映射也可以。

For what it's worth, my basic test case / expectation of behaviour is that the following will be equal (up to the equivalence of null = "Not present") for any two maps a and b:

对于它的价值,我的基本测试用例/行为期望是,对于任何两个映射 a 和 b,以下将是相等的(直到 null = "Not present" 的等价):

a 
(merge b (difference a b))

What would be the best way to implement this?

实现这一点的最佳方法是什么?

采纳答案by Micha? Marczyk

I'm not sure what the absolutely most efficient way to do this is, but here's a couple of things which may be useful:

我不确定最有效的方法是什么,但这里有一些可能有用的东西:

  1. The basic expectation of behaviour from the question text is impossible: if aand bare maps such that bcontains at least one key not present in a, (merge b <sth>)cannot be equal to a.

  2. If you end up going with an interop solution but then need to go back to a PersistentHashMapat some point, there's always

    (clojure.lang.PersistentHashMap/create
     (doto (java.util.HashMap.)
       (.put :foo 1)
       (.put :bar 2)))
    ; => {:foo 1 :bar 2}
    
  3. If you need to pass the keyset of a Clojure map to a Java method, you can use

    (.keySet {:foo 1 :bar 2})
    ; => #< [:foo, :bar]>
    
  4. If all keys involved are guaranteed to be Comparable, this could be exploited for efficient computation of differenceon maps with many keys (sort & merge scan). For unconstrained keys this is of course a no-go and for small maps it could actually hurt performance.

  5. It's good to have a version written in Clojure, if only to set a baseline performance expectation. Here is one: (updated)

    (defn map-difference [m1 m2]
            (loop [m (transient {})
                   ks (concat (keys m1) (keys m2))]
              (if-let [k (first ks)]
                (let [e1 (find m1 k)
                      e2 (find m2 k)]
                  (cond (and e1 e2 (not= (e1 1) (e2 1))) (recur (assoc! m k (e1 1)) (next ks))
                        (not e1) (recur (assoc! m k (e2 1)) (next ks))
                        (not e2) (recur (assoc! m k (e1 1)) (next ks))
                        :else    (recur m (next ks))))
                (persistent! m))))
    

    I think that just doing (concat (keys m1) (keys m2))and possibly duplicating some work is likely more efficient most of the time than checking a given key is in "the other map" too at every step.

  1. 问题文本的基本行为期望是不可能的:如果ab是这样的映射,其中b包含至少一个不存在于 中的键a(merge b <sth>)则不能等于a

  2. 如果您最终选择了互操作解决方案,但随后又需要返回到PersistentHashMap某个点,那么总是有

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

    (.keySet {:foo 1 :bar 2})
    ; => #< [:foo, :bar]>
    
  4. 如果保证所有涉及的键都是Comparable,则可以利用它来高效计算difference具有许多键的映射(排序和合并扫描)。对于不受约束的键,这当然是行不通的,对于小地图,它实际上可能会损害性能。

  5. 有一个用 Clojure 编写的版本是很好的,如果只是为了设置一个基线性能期望。这是一个:(更新)

    (defn map-difference [m1 m2]
            (loop [m (transient {})
                   ks (concat (keys m1) (keys m2))]
              (if-let [k (first ks)]
                (let [e1 (find m1 k)
                      e2 (find m2 k)]
                  (cond (and e1 e2 (not= (e1 1) (e2 1))) (recur (assoc! m k (e1 1)) (next ks))
                        (not e1) (recur (assoc! m k (e2 1)) (next ks))
                        (not e2) (recur (assoc! m k (e1 1)) (next ks))
                        :else    (recur m (next ks))))
                (persistent! m))))
    

    我认为在(concat (keys m1) (keys m2))大多数情况下,仅仅做一些工作并可能重复一些工作可能比在每一步检查给定的键也在“另一个地图”中更有效。

To wrap up the answer, here's a very simple-minded set-based version with the nice property that it says what it does -- if I misunderstood the spec, it should be readily apparent here. :-)

总结一下答案,这是一个非常简单的基于集合的版本,它具有很好的属性,它说明了它的作用——如果我误解了规范,那么这里应该很明显。:-)

(defn map-difference [m1 m2]
  (let [ks1 (set (keys m1))
        ks2 (set (keys m2))
        ks1-ks2 (set/difference ks1 ks2)
        ks2-ks1 (set/difference ks2 ks1)
        ks1*ks2 (set/intersection ks1 ks2)]
    (merge (select-keys m1 ks1-ks2)
           (select-keys m2 ks2-ks1)
           (select-keys m1
                        (remove (fn [k] (= (m1 k) (m2 k)))
                                ks1*ks2)))))

回答by Sylar

In Java, Google Commons Collections offer a quite performant solution.

在 Java 中,Google Commons Collections 提供了一个非常高效的解决方案

回答by Aaron Digulla

Use the built-in collections API:

使用内置的集合 API:

Set<Map.Entry<K,V>> difference = a.entrySet().removeAll(b.entrySet());

If you need to convert that back into a map, you must iterate. In that case, I suggest:

如果您需要将其转换回地图,则必须进行迭代。在这种情况下,我建议:

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());
    }
}

回答by nickik

  1. Clojure maps will be fine because reading clojure maps is very fast.

  2. I can't answer you but I can give you something to look at. Brenton Ashworth made a testtool where he solved the problem with map compares. Maybe you can look at his code to get hint for implementation. http://formpluslogic.blogspot.com/2010/07/better-clojure-test-results-with-deview.htmland http://github.com/brentonashworth/deview

  3. I don't think there is a better implementation that compare the keys and look up if the are different.

  1. Clojure 映射会很好,因为读取 clojure 映射非常快。

  2. 我不能回答你,但我可以给你一些东西看。Brenton Ashworth 制作了一个测试工具,他用地图比较解决了这个问题。也许您可以查看他的代码以获取实现提示。http://formpluslogic.blogspot.com/2010/07/better-clojure-test-results-with-deview.htmlhttp://github.com/brentonashworth/deview

  3. 我认为没有更好的实现来比较键并查找键是否不同。

回答by Adam Schmideg

I am not sure about its performance

我不确定它的性能

(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)))

I used :missingkey to avoid ambiguity between a nilvalue in the original map, and a missing key -- you can of course change it to nil.

我使用:missingkey 来避免nil原始映射中的值与丢失的键之间的歧义——您当然可以将其更改为nil.

回答by Andrejs

You could also just use Maps.difference(..)method from Google's Guava libraries

您也可以使用Google 的 Guava 库中的Maps.difference(..)方法

回答by sdasdadas

What about...

关于什么...

(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)))))