diff --git a/src/org/openstreetmap/josm/data/osm/TagCollection.java b/src/org/openstreetmap/josm/data/osm/TagCollection.java
index b8324ae..5118c52 100644
--- a/src/org/openstreetmap/josm/data/osm/TagCollection.java
+++ b/src/org/openstreetmap/josm/data/osm/TagCollection.java
@@ -3,6 +3,7 @@ package org.openstreetmap.josm.data.osm;
 
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -14,8 +15,11 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.tools.Utils;
@@ -44,7 +48,9 @@ import org.openstreetmap.josm.tools.Utils;
  *
  * @since 2008
  */
-public class TagCollection implements Iterable<Tag> {
+public class TagCollection implements Iterable<Tag>, Serializable {
+
+    private static final long serialVersionUID = 1;
 
     /**
      * Creates a tag collection from the tags managed by a specific
@@ -145,7 +151,7 @@ public class TagCollection implements Iterable<Tag> {
         return tags;
     }
 
-    private final Set<Tag> tags = new HashSet<>();
+    private final Map<Tag, Integer> tags = new HashMap<>();
 
     /**
      * Creates an empty tag collection.
@@ -162,7 +168,7 @@ public class TagCollection implements Iterable<Tag> {
      */
     public TagCollection(TagCollection other) {
         if (other != null) {
-            tags.addAll(other.tags);
+            tags.putAll(other.tags);
         }
     }
 
@@ -199,9 +205,18 @@ public class TagCollection implements Iterable<Tag> {
      * @param tag the tag to add
      */
     public final void add(Tag tag) {
-        if (tag == null) return;
-        if (tags.contains(tag)) return;
-        tags.add(tag);
+        if (tag != null) {
+            tags.merge(tag, 1, (i, j) -> i + j);
+        }
+    }
+
+    /**
+     * Gets the number of this this tag was added to the collection.
+     * @param tag The tag
+     * @return The number of thimes this tag is used in this collection.
+     */
+    public int getTagOccurence(Tag tag) {
+        return tags.getOrDefault(tag, 0);
     }
 
     /**
@@ -224,8 +239,11 @@ public class TagCollection implements Iterable<Tag> {
      * @param tags the other tag collection
      */
     public final void add(TagCollection tags) {
-        if (tags == null) return;
-        this.tags.addAll(tags.tags);
+        if (tags != null) {
+            for (Entry<Tag, Integer> entry : tags.tags.entrySet()) {
+                this.tags.merge(entry.getKey(), entry.getValue(), (i, j) -> i + j);
+            }
+        }
     }
 
     /**
@@ -246,8 +264,9 @@ public class TagCollection implements Iterable<Tag> {
      * @param tags the tags to be removed
      */
     public void remove(Collection<Tag> tags) {
-        if (tags == null) return;
-        this.tags.removeAll(tags);
+        if (tags != null) {
+            tags.stream().forEach(this::remove);
+        }
     }
 
     /**
@@ -257,8 +276,9 @@ public class TagCollection implements Iterable<Tag> {
      * @param tags the tag collection to be removed.
      */
     public void remove(TagCollection tags) {
-        if (tags == null) return;
-        this.tags.removeAll(tags.tags);
+        if (tags != null) {
+            tags.tags.keySet().stream().forEach(this::remove);
+        }
     }
 
     /**
@@ -268,11 +288,12 @@ public class TagCollection implements Iterable<Tag> {
      * @param key the key to be removed
      */
     public void removeByKey(String key) {
-        if (key == null) return;
-        Iterator<Tag> it = tags.iterator();
-        while (it.hasNext()) {
-            if (it.next().matchesKey(key)) {
-                it.remove();
+        if (key != null) {
+            Iterator<Tag> it = tags.keySet().iterator();
+            while (it.hasNext()) {
+                if (it.next().matchesKey(key)) {
+                    it.remove();
+                }
             }
         }
     }
@@ -297,7 +318,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return true if the this tag collection contains <code>tag</code>; false, otherwise
      */
     public boolean contains(Tag tag) {
-        return tags.contains(tag);
+        return tags.containsKey(tag);
     }
 
     /**
@@ -305,13 +326,11 @@ public class TagCollection implements Iterable<Tag> {
      *
      * @param key the key to look up
      * @return true if this tag collection contains at least one tag with key <code>key</code>; false, otherwise
+     * @deprecated Use {@link #hasTagsFor(String)} instead.
      */
+    @Deprecated
     public boolean containsKey(String key) {
-        if (key == null) return false;
-        for (Tag tag: tags) {
-            if (tag.matchesKey(key)) return true;
-        }
-        return false;
+        return generateStreamForKey(key).findAny().isPresent();
     }
 
     /**
@@ -323,8 +342,11 @@ public class TagCollection implements Iterable<Tag> {
      * false, if tags is null.
      */
     public boolean containsAll(Collection<Tag> tags) {
-        if (tags == null) return false;
-        return this.tags.containsAll(tags);
+        if (tags == null) {
+            return false;
+        } else {
+            return this.tags.keySet().containsAll(tags);
+        }
     }
 
     /**
@@ -335,31 +357,21 @@ public class TagCollection implements Iterable<Tag> {
      * @return true if this tag collection at least one tag for every key in <code>keys</code>.
      */
     public boolean containsAllKeys(Collection<String> keys) {
-        if (keys == null) return false;
-        for (String key: keys) {
-            if (key == null) {
-                continue;
-            }
-            if (!containsKey(key)) return false;
+        if (keys == null) {
+            return false;
+        } else {
+            return keys.stream().filter(Objects::nonNull).allMatch(this::hasTagsFor);
         }
-        return true;
     }
 
     /**
      * Replies the number of tags with key <code>key</code>
      *
      * @param key the key to look up
-     * @return the number of tags with key <code>key</code>. 0, if key is null.
+     * @return the number of tags with key <code>key</code>, including the empty "" value. 0, if key is null.
      */
     public int getNumTagsFor(String key) {
-        if (key == null) return 0;
-        int count = 0;
-        for (Tag tag: tags) {
-            if (tag.matchesKey(key)) {
-                count++;
-            }
-        }
-        return count;
+        return (int) generateStreamForKey(key).count();
     }
 
     /**
@@ -380,10 +392,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return true it there is at least one tag with a non empty value for key.
      */
     public boolean hasValuesFor(String key) {
-        if (key == null) return false;
-        Set<String> values = getTagsFor(key).getValues();
-        values.remove("");
-        return !values.isEmpty();
+        return generateStreamForKey(key).filter(t -> !t.getValue().isEmpty()).findAny().isPresent();
     }
 
     /**
@@ -396,9 +405,7 @@ public class TagCollection implements Iterable<Tag> {
      * if the value of this tag is not empty
      */
     public boolean hasUniqueNonEmptyValue(String key) {
-        if (key == null) return false;
-        Set<String> values = getTagsFor(key).getValues();
-        return values.size() == 1 && !values.contains("");
+        return generateStreamForKey(key).filter(t -> !t.getValue().isEmpty()).count() == 1;
     }
 
     /**
@@ -409,9 +416,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return true if there is a tag with an empty value for <code>key</code>
      */
     public boolean hasEmptyValue(String key) {
-        if (key == null) return false;
-        Set<String> values = getTagsFor(key).getValues();
-        return values.contains("");
+        return generateStreamForKey(key).anyMatch(t -> t.getValue().isEmpty());
     }
 
     /**
@@ -423,8 +428,7 @@ public class TagCollection implements Iterable<Tag> {
      * the value for this tag is empty
      */
     public boolean hasUniqueEmptyValue(String key) {
-        if (key == null) return false;
-        Set<String> values = getTagsFor(key).getValues();
+        Set<String> values = getValues(key);
         return values.size() == 1 && values.contains("");
     }
 
@@ -438,13 +442,7 @@ public class TagCollection implements Iterable<Tag> {
      */
     public TagCollection getTagsFor(String key) {
         TagCollection ret = new TagCollection();
-        if (key == null)
-            return ret;
-        for (Tag tag: tags) {
-            if (tag.matchesKey(key)) {
-                ret.add(tag);
-            }
-        }
+        generateStreamForKey(key).forEach(ret::add);
         return ret;
     }
 
@@ -474,17 +472,17 @@ public class TagCollection implements Iterable<Tag> {
      * @return the tags of this tag collection as set
      */
     public Set<Tag> asSet() {
-        return new HashSet<>(tags);
+        return new HashSet<>(tags.keySet());
     }
 
     /**
      * Replies the tags of this tag collection as list.
      * Note that the order of the list is not preserved between method invocations.
      *
-     * @return the tags of this tag collection as list.
+     * @return the tags of this tag collection as list. There are no dupplicate values.
      */
     public List<Tag> asList() {
-        return new ArrayList<>(tags);
+        return new ArrayList<>(tags.keySet());
     }
 
     /**
@@ -494,7 +492,7 @@ public class TagCollection implements Iterable<Tag> {
      */
     @Override
     public Iterator<Tag> iterator() {
-        return tags.iterator();
+        return tags.keySet().iterator();
     }
 
     /**
@@ -503,11 +501,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return the set of keys of this tag collection
      */
     public Set<String> getKeys() {
-        Set<String> ret = new HashSet<>();
-        for (Tag tag: tags) {
-            ret.add(tag.getKey());
-        }
-        return ret;
+        return generateKeyStream().collect(Collectors.toCollection(HashSet::new));
     }
 
     /**
@@ -516,18 +510,8 @@ public class TagCollection implements Iterable<Tag> {
      * @return the set of keys which have at least 2 matching tags.
      */
     public Set<String> getKeysWithMultipleValues() {
-        Map<String, Integer> counters = new HashMap<>();
-        for (Tag tag: tags) {
-            Integer v = counters.get(tag.getKey());
-            counters.put(tag.getKey(), (v == null) ? 1 : v+1);
-        }
-        Set<String> ret = new HashSet<>();
-        for (Entry<String, Integer> e : counters.entrySet()) {
-            if (e.getValue() > 1) {
-                ret.add(e.getKey());
-            }
-        }
-        return ret;
+        HashSet<String> singleKeys = new HashSet<>();
+        return generateKeyStream().filter(key -> !singleKeys.add(key)).collect(Collectors.toSet());
     }
 
     /**
@@ -561,11 +545,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return the set of values
      */
     public Set<String> getValues() {
-        Set<String> ret = new HashSet<>();
-        for (Tag tag: tags) {
-            ret.add(tag.getValue());
-        }
-        return ret;
+        return tags.keySet().stream().map(e -> e.getValue()).collect(Collectors.toSet());
     }
 
     /**
@@ -577,14 +557,8 @@ public class TagCollection implements Iterable<Tag> {
      * are no values for the given key
      */
     public Set<String> getValues(String key) {
-        Set<String> ret = new HashSet<>();
-        if (key == null) return ret;
-        for (Tag tag: tags) {
-            if (tag.matchesKey(key)) {
-                ret.add(tag.getValue());
-            }
-        }
-        return ret;
+        // null-safe
+        return generateStreamForKey(key).map(e -> e.getValue()).collect(Collectors.toSet());
     }
 
     /**
@@ -593,7 +567,7 @@ public class TagCollection implements Iterable<Tag> {
      * @return {@code true} if for every key there is one tag only
      */
     public boolean isApplicableToPrimitive() {
-        return size() == getKeys().size();
+        return getKeysWithMultipleValues().isEmpty();
     }
 
     /**
@@ -606,9 +580,8 @@ public class TagCollection implements Iterable<Tag> {
      */
     public void applyTo(Tagged primitive) {
         if (primitive == null) return;
-        if (!isApplicableToPrimitive())
-            throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
-        for (Tag tag: tags) {
+        ensureApplicableToPrimitive();
+        for (Tag tag: tags.keySet()) {
             if (tag.getValue() == null || tag.getValue().isEmpty()) {
                 primitive.remove(tag.getKey());
             } else {
@@ -627,8 +600,7 @@ public class TagCollection implements Iterable<Tag> {
      */
     public void applyTo(Collection<? extends Tagged> primitives) {
         if (primitives == null) return;
-        if (!isApplicableToPrimitive())
-            throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
+        ensureApplicableToPrimitive();
         for (Tagged primitive: primitives) {
             applyTo(primitive);
         }
@@ -644,10 +616,9 @@ public class TagCollection implements Iterable<Tag> {
      */
     public void replaceTagsOf(Tagged primitive) {
         if (primitive == null) return;
-        if (!isApplicableToPrimitive())
-            throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
+        ensureApplicableToPrimitive();
         primitive.removeAll();
-        for (Tag tag: tags) {
+        for (Tag tag: tags.keySet()) {
             primitive.put(tag.getKey(), tag.getValue());
         }
     }
@@ -662,27 +633,27 @@ public class TagCollection implements Iterable<Tag> {
      */
     public void replaceTagsOf(Collection<? extends Tagged> primitives) {
         if (primitives == null) return;
-        if (!isApplicableToPrimitive())
-            throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
+        ensureApplicableToPrimitive();
         for (Tagged primitive: primitives) {
             replaceTagsOf(primitive);
         }
     }
 
+    private void ensureApplicableToPrimitive() {
+        if (!isApplicableToPrimitive())
+            throw new IllegalStateException(tr("Tag collection cannot be applied to a primitive because there are keys with multiple values."));
+    }
+
     /**
      * Builds the intersection of this tag collection and another tag collection
      *
      * @param other the other tag collection. If null, replies an empty tag collection.
-     * @return the intersection of this tag collection and another tag collection
+     * @return the intersection of this tag collection and another tag collection. All counts are set to 1.
      */
     public TagCollection intersect(TagCollection other) {
         TagCollection ret = new TagCollection();
         if (other != null) {
-            for (Tag tag: tags) {
-                if (other.contains(tag)) {
-                    ret.add(tag);
-                }
-            }
+            tags.keySet().stream().filter(other::contains).forEach(ret::add);
         }
         return ret;
     }
@@ -705,7 +676,7 @@ public class TagCollection implements Iterable<Tag> {
      * Replies the union of this tag collection and another tag collection
      *
      * @param other the other tag collection. May be null.
-     * @return the union of this tag collection and another tag collection
+     * @return the union of this tag collection and another tag collection. The tag count is summed.
      */
     public TagCollection union(TagCollection other) {
         TagCollection ret = new TagCollection(this);
@@ -757,10 +728,10 @@ public class TagCollection implements Iterable<Tag> {
     }
 
     /**
-     * Replies the sum of all numeric tag values.
+     * Replies the sum of all numeric tag values. Ignores dupplicates.
      * @param key the key to look up
      *
-     * @return the sum of all numeric tag values, as string
+     * @return the sum of all numeric tag values, as string.
      * @since 7743
      */
     public String getSummedValues(String key) {
@@ -775,6 +746,19 @@ public class TagCollection implements Iterable<Tag> {
         return Integer.toString(result);
     }
 
+    private Stream<String> generateKeyStream() {
+        return tags.keySet().stream().map(tag -> tag.getKey());
+    }
+
+    /**
+     * Get a stram for the given key.
+     * @param key The key
+     * @return The stream. An empty stream if key is <code>null</code>
+     */
+    private Stream<Tag> generateStreamForKey(String key) {
+        return tags.keySet().stream().filter(e -> e.matchesKey(key));
+    }
+
     @Override
     public String toString() {
         return tags.toString();
diff --git a/src/org/openstreetmap/josm/data/osm/TagMap.java b/src/org/openstreetmap/josm/data/osm/TagMap.java
index 12a1dd6..c658333 100644
--- a/src/org/openstreetmap/josm/data/osm/TagMap.java
+++ b/src/org/openstreetmap/josm/data/osm/TagMap.java
@@ -6,6 +6,7 @@ import java.util.AbstractMap;
 import java.util.AbstractSet;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.List;
@@ -150,6 +151,16 @@ public class TagMap extends AbstractMap<String, String> implements Serializable
         }
     }
 
+    /**
+     * Creates a new map using the given list of tags. For dupplicate keys the last value found is used.
+     * @param tags The tags
+     */
+    public TagMap(Collection<Tag> tags) {
+        for(Tag tag : tags) {
+            put(tag.getKey(), tag.getValue());
+        }
+    }
+
     @Override
     public Set<Entry<String, String>> entrySet() {
         return new TagEntrySet(tags);
diff --git a/src/org/openstreetmap/josm/data/osm/Tagged.java b/src/org/openstreetmap/josm/data/osm/Tagged.java
index fb9cf44..5cf94a2 100644
--- a/src/org/openstreetmap/josm/data/osm/Tagged.java
+++ b/src/org/openstreetmap/josm/data/osm/Tagged.java
@@ -34,6 +34,15 @@ public interface Tagged {
     void put(String key, String value);
 
     /**
+     * Sets a key/value pairs
+     *
+     * @param tag The tag to set.
+     */
+    default void put(Tag tag) {
+        put(tag.getKey(), tag.getValue());
+    }
+
+    /**
      * Replies the value of the given key; null, if there is no value for this key
      *
      * @param key the key
diff --git a/test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java b/test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java
new file mode 100644
index 0000000..db8c5ed
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java
@@ -0,0 +1,736 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Tests of {@link TagCollection}.
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class TagCollectionTest {
+    private final Tag tagA = new Tag("k", "v");
+    private final Tag tagB = new Tag("k", "b");
+    private final Tag tagC = new Tag("k2", "b");
+    private final Tag tagD = new Tag("k3", "c");
+    private final Tag tagEmpty = new Tag("k", "");
+    private final Tag tagNullKey = new Tag(null, "b");
+    private final Tag tagNullValue = new Tag("k2", null);
+
+    /**
+     * We need prefs for using primitives
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().preferences();
+
+    private void assertTagCounts(TagCollection collection, int a, int b, int c, int d) {
+        assertEquals(a, collection.getTagOccurence(tagA));
+        assertEquals(b, collection.getTagOccurence(tagB));
+        assertEquals(c, collection.getTagOccurence(tagC));
+        assertEquals(d, collection.getTagOccurence(tagD));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#from(org.openstreetmap.josm.data.osm.Tagged)}.
+     */
+    @Test
+    public void testFromTagged() {
+        TagCollection c = TagCollection.from(tagA);
+        assertTagCounts(c, 1, 0, 0, 0);
+
+        NodeData p1 = new NodeData();
+        p1.put(tagA);
+        p1.put(tagC);
+        TagCollection d = TagCollection.from(p1);
+        assertTagCounts(d, 1, 0, 1, 0);
+
+        TagCollection e = TagCollection.from((Tagged) null);
+        assertTagCounts(e, 0, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#from(java.util.Map)}.
+     */
+    @Test
+    public void testFromMapOfStringString() {
+        TagCollection c = TagCollection.from(tagA.getKeys());
+        assertTagCounts(c, 1, 0, 0, 0);
+
+        HashMap<String, String> map = new HashMap<>();
+        map.putAll(tagA.getKeys());
+        map.putAll(tagC.getKeys());
+        TagCollection d = TagCollection.from(map);
+        assertTagCounts(d, 1, 0, 1, 0);
+
+        TagCollection e = TagCollection.from((Map<String, String>) null);
+        assertTagCounts(e, 0, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#unionOfAllPrimitives(java.util.Collection)}.
+     */
+    @Test
+    public void testUnionOfAllPrimitivesCollectionOfQextendsTagged() {
+        TagCollection c = TagCollection.unionOfAllPrimitives(Arrays.asList(tagA));
+        assertEquals(1, c.getTagOccurence(tagA));
+
+        TagCollection d = TagCollection.unionOfAllPrimitives(Arrays.asList(tagA, tagC));
+        assertTagCounts(d, 1, 0, 1, 0);
+
+        TagCollection e = TagCollection.unionOfAllPrimitives((Collection<? extends Tagged>) null);
+        assertTagCounts(e, 0, 0, 0, 0);
+
+        TagCollection f = TagCollection.unionOfAllPrimitives(Arrays.<Tagged>asList());
+        assertTagCounts(f, 0, 0, 0, 0);
+
+        TagCollection g = TagCollection.unionOfAllPrimitives(Arrays.asList(tagA, tagC, tagC, null));
+        assertTagCounts(g, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#commonToAllPrimitives(java.util.Collection)}.
+     */
+    @Test
+    @Ignore("TODO")
+    public void testCommonToAllPrimitives() {
+        fail("Not yet implemented");
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#unionOfAllPrimitives(org.openstreetmap.josm.data.osm.DataSet)}.
+     */
+    @Test
+    @Ignore("TODO")
+    public void testUnionOfAllPrimitivesDataSet() {
+        fail("Not yet implemented");
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#TagCollection()}.
+     */
+    @Test
+    public void testTagCollection() {
+        TagCollection c = new TagCollection();
+        assertTagCounts(c, 0, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#TagCollection(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testTagCollectionTagCollection() {
+        TagCollection blueprint = TagCollection.unionOfAllPrimitives(Arrays.asList(tagA, tagC, tagC));
+        TagCollection c = new TagCollection(blueprint);
+        assertTagCounts(c, 1, 0, 2, 0);
+
+        TagCollection d = new TagCollection((TagCollection) null);
+        assertTagCounts(d, 0, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#TagCollection(java.util.Collection)}.
+     */
+    @Test
+    public void testTagCollectionCollectionOfTag() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertTagCounts(c, 1, 0, 2, 0);
+
+        TagCollection d = new TagCollection((Collection<Tag>) null);
+        assertTagCounts(d, 0, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#size()}.
+     */
+    @Test
+    public void testSize() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertEquals(2, c.size());
+
+        TagCollection d = new TagCollection();
+        assertEquals(0, d.size());
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#isEmpty()}.
+     */
+    @Test
+    public void testIsEmpty() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertFalse(c.isEmpty());
+
+        TagCollection d = new TagCollection();
+        assertTrue(d.isEmpty());
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#add(org.openstreetmap.josm.data.osm.Tag)}.
+     */
+    @Test
+    public void testAddTag() {
+        TagCollection c = new TagCollection();
+        assertTagCounts(c, 0, 0, 0, 0);
+        c.add(tagC);
+        assertTagCounts(c, 0, 0, 1, 0);
+        c.add(tagA);
+        c.add(tagC);
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.add((Tag) null);
+        assertTagCounts(c, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getTagOccurence(org.openstreetmap.josm.data.osm.Tag)}.
+     */
+    @Test
+    public void testGetTagCount() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertEquals(2, c.getTagOccurence(tagC));
+        assertEquals(0, c.getTagOccurence(tagB));
+        assertEquals(0, c.getTagOccurence(tagNullKey));
+        assertEquals(0, c.getTagOccurence(tagNullValue));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#add(java.util.Collection)}.
+     */
+    @Test
+    public void testAddCollectionOfTag() {
+        TagCollection c = new TagCollection();
+        assertTagCounts(c, 0, 0, 0, 0);
+        c.add(Arrays.asList(tagC));
+        assertTagCounts(c, 0, 0, 1, 0);
+        c.add(Arrays.asList(tagA, tagC));
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.add(Collections.emptyList());
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.add((Collection<Tag>) null);
+        assertTagCounts(c, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#add(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testAddTagCollection() {
+        TagCollection c = new TagCollection();
+        assertTagCounts(c, 0, 0, 0, 0);
+        c.add(new TagCollection(Arrays.asList(tagC)));
+        assertTagCounts(c, 0, 0, 1, 0);
+        c.add(new TagCollection(Arrays.asList(tagA, tagC)));
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.add(new TagCollection());
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.add((TagCollection) null);
+        assertTagCounts(c, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#remove(org.openstreetmap.josm.data.osm.Tag)}.
+     */
+    @Test
+    public void testRemoveTag() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.remove(tagC);
+        assertTagCounts(c, 1, 0, 0, 0);
+        c.remove(tagB);
+        assertTagCounts(c, 1, 0, 0, 0);
+        c.remove((Tag) null);
+        assertTagCounts(c, 1, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#remove(java.util.Collection)}.
+     */
+    @Test
+    public void testRemoveCollectionOfTag() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.remove(Arrays.asList(tagC, tagB));
+        assertTagCounts(c, 1, 0, 0, 0);
+        c.remove((Collection<Tag>) null);
+        assertTagCounts(c, 1, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#remove(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testRemoveTagCollection() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagC));
+        assertTagCounts(c, 1, 0, 2, 0);
+        c.remove(new TagCollection(Arrays.asList(tagC, tagB)));
+        assertTagCounts(c, 1, 0, 0, 0);
+        c.remove(new TagCollection());
+        assertTagCounts(c, 1, 0, 0, 0);
+        c.remove((TagCollection) null);
+        assertTagCounts(c, 1, 0, 0, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#removeByKey(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveByKeyString() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagB, tagC));
+        assertTagCounts(c, 1, 2, 1, 0);
+        c.removeByKey("k");
+        assertTagCounts(c, 0, 0, 1, 0);
+        c.removeByKey((String) null);
+        assertTagCounts(c, 0, 0, 1, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#removeByKey(java.util.Collection)}.
+     */
+    @Test
+    public void testRemoveByKeyCollectionOfString() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagB, tagC, tagD));
+        assertTagCounts(c, 1, 2, 1, 1);
+        c.removeByKey(Arrays.asList("k", "k2", null));
+        assertTagCounts(c, 0, 0, 0, 1);
+        c.removeByKey((Collection<String>) null);
+        assertTagCounts(c, 0, 0, 0, 1);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#contains(org.openstreetmap.josm.data.osm.Tag)}.
+     */
+    @Test
+    public void testContains() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagB));
+        assertTrue(c.contains(tagA));
+        assertTrue(c.contains(tagB));
+        assertFalse(c.contains(tagC));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#containsAll(java.util.Collection)}.
+     */
+    @Test
+    public void testContainsAll() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagB));
+        assertTrue(c.containsAll(Arrays.asList(tagA, tagB)));
+        assertFalse(c.containsAll(Arrays.asList(tagA, tagC)));
+        assertTrue(c.containsAll(Arrays.asList()));
+        assertFalse(c.containsAll(null));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#containsAllKeys(java.util.Collection)}.
+     */
+    @Test
+    public void testContainsAllKeys() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagC));
+        assertTrue(c.containsAllKeys(Arrays.asList("k", "k2")));
+        assertFalse(c.containsAllKeys(Arrays.asList("k", "k3")));
+        assertTrue(c.containsAllKeys(Arrays.asList()));
+        assertFalse(c.containsAllKeys(null));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getNumTagsFor(java.lang.String)}.
+     */
+    @Test
+    public void testGetNumTagsFor() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagC));
+        assertEquals(2, c.getNumTagsFor("k"));
+        assertEquals(1, c.getNumTagsFor("k2"));
+        assertEquals(0, c.getNumTagsFor("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#hasTagsFor(java.lang.String)}.
+     */
+    @Test
+    public void testHasTagsFor() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagB, tagC));
+        assertTrue(c.hasTagsFor("k"));
+        assertTrue(c.hasTagsFor("k2"));
+        assertFalse(c.hasTagsFor("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#hasValuesFor(java.lang.String)}.
+     */
+    @Test
+    public void testHasValuesFor() {
+        TagCollection c = new TagCollection(Arrays.asList(tagC, tagEmpty));
+        assertFalse(c.hasValuesFor("k"));
+        assertTrue(c.hasValuesFor("k2"));
+        assertFalse(c.hasValuesFor("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#hasUniqueNonEmptyValue(java.lang.String)}.
+     */
+    @Test
+    public void testHasUniqueNonEmptyValue() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagEmpty));
+        assertTrue(c.hasUniqueNonEmptyValue("k"));
+        assertTrue(c.hasUniqueNonEmptyValue("k2"));
+        assertFalse(c.hasUniqueNonEmptyValue("k3"));
+
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagEmpty));
+        assertFalse(d.hasUniqueNonEmptyValue("k"));
+        assertTrue(d.hasUniqueNonEmptyValue("k2"));
+        assertFalse(d.hasUniqueNonEmptyValue("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#hasEmptyValue(java.lang.String)}.
+     */
+    @Test
+    public void testHasEmptyValue() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC, tagEmpty));
+        assertTrue(c.hasEmptyValue("k"));
+        assertFalse(c.hasEmptyValue("k2"));
+        assertFalse(c.hasEmptyValue("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#hasUniqueEmptyValue(java.lang.String)}.
+     */
+    @Test
+    public void testHasUniqueEmptyValue() {
+        TagCollection c = new TagCollection(Arrays.asList(tagC, tagEmpty));
+        assertTrue(c.hasUniqueEmptyValue("k"));
+        assertFalse(c.hasUniqueEmptyValue("k2"));
+        assertFalse(c.hasUniqueEmptyValue("k3"));
+
+        TagCollection d = new TagCollection(Arrays.asList());
+        assertFalse(d.hasUniqueEmptyValue("k"));
+        assertFalse(d.hasUniqueEmptyValue("k2"));
+        assertFalse(d.hasUniqueEmptyValue("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getTagsFor(java.lang.String)}.
+     */
+    @Test
+    public void testGetTagsForString() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagEmpty));
+        TagCollection collection = d.getTagsFor("k");
+        assertTagCounts(collection, 1, 1, 0, 0);
+        assertEquals(1, collection.getTagOccurence(tagEmpty));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getTagsFor(java.util.Collection)}.
+     */
+    @Test
+    public void testGetTagsForCollectionOfString() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagEmpty));
+        TagCollection collection = d.getTagsFor(Arrays.asList("k", "k2"));
+        assertTagCounts(collection, 1, 1, 1, 0);
+        assertEquals(1, collection.getTagOccurence(tagEmpty));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#asSet()}.
+     */
+    @Test
+    public void testAsSet() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagC));
+        Set<Tag> set = d.asSet();
+        assertEquals(3, set.size());
+        assertTrue(set.contains(tagA));
+        assertTrue(set.contains(tagB));
+        assertTrue(set.contains(tagC));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#asList()}.
+     */
+    @Test
+    public void testAsList() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagC));
+        List<Tag> set = d.asList();
+        assertEquals(3, set.size());
+        assertTrue(set.contains(tagA));
+        assertTrue(set.contains(tagB));
+        assertTrue(set.contains(tagC));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#iterator()}.
+     */
+    @Test
+    public void testIterator() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA));
+        Iterator<Tag> it = d.iterator();
+        assertTrue(it.hasNext());
+        assertEquals(tagA, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getKeys()}.
+     */
+    @Test
+    public void testGetKeys() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagC));
+        Set<String> set = d.getKeys();
+        assertEquals(2, set.size());
+        assertTrue(set.contains("k"));
+        assertTrue(set.contains("k2"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getKeysWithMultipleValues()}.
+     */
+    @Test
+    public void testGetKeysWithMultipleValues() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagB, tagC, tagC));
+        Set<String> set = d.getKeysWithMultipleValues();
+        assertEquals(1, set.size());
+        assertTrue(set.contains("k"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#setUniqueForKey(org.openstreetmap.josm.data.osm.Tag)}.
+     */
+    @Test
+    public void testSetUniqueForKeyTag() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagA, tagB, tagC, tagC));
+        assertTagCounts(d, 2, 1, 2, 0);
+        d.setUniqueForKey(tagA);
+        assertTagCounts(d, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#setUniqueForKey(java.lang.String, java.lang.String)}.
+     */
+    @Test
+    public void testSetUniqueForKeyStringString() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagA, tagB, tagC, tagC));
+        assertTagCounts(d, 2, 1, 2, 0);
+        d.setUniqueForKey(tagA.getKey(), tagA.getValue());
+        assertTagCounts(d, 1, 0, 2, 0);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getValues()}.
+     */
+    @Test
+    public void testGetValues() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagA, tagB, tagC, tagEmpty));
+        Set<String> set = d.getValues();
+        assertEquals(3, set.size());
+        assertTrue(set.contains("v"));
+        assertTrue(set.contains("b"));
+        assertTrue(set.contains(""));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getValues(java.lang.String)}.
+     */
+    @Test
+    public void testGetValuesString() {
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagA, tagC, tagEmpty));
+        Set<String> set = d.getValues("k");
+        assertEquals(2, set.size());
+        assertTrue(set.contains("v"));
+        assertTrue(set.contains(""));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#isApplicableToPrimitive()}.
+     */
+    @Test
+    public void testIsApplicableToPrimitive() {
+        TagCollection c = new TagCollection();
+        assertTrue(c.isApplicableToPrimitive());
+        TagCollection d = new TagCollection(Arrays.asList(tagA, tagA, tagC, tagEmpty));
+        assertFalse(d.isApplicableToPrimitive());
+        TagCollection e = new TagCollection(Arrays.asList(tagA, tagC));
+        assertTrue(e.isApplicableToPrimitive());
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#applyTo(org.openstreetmap.josm.data.osm.Tagged)}.
+     */
+    @Test
+    public void testApplyToTagged() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC));
+        NodeData tagged = new NodeData();
+        tagged.put("k", "x");
+        tagged.put("k3", "x");
+        c.applyTo(tagged);
+        assertEquals("v", tagged.get("k"));
+        assertEquals("b", tagged.get("k2"));
+        assertEquals("x", tagged.get("k3"));
+        TagCollection d = new TagCollection(Arrays.asList(tagEmpty));
+        d.applyTo(tagged);
+        assertEquals(null, tagged.get("k"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#applyTo(java.util.Collection)}.
+     */
+    @Test
+    public void testApplyToCollectionOfQextendsTagged() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC));
+        NodeData tagged = new NodeData();
+        NodeData tagged2 = new NodeData();
+        tagged2.put("k", "x");
+        tagged2.put("k3", "x");
+        c.applyTo(Arrays.asList(tagged, tagged2));
+        assertEquals("v", tagged.get("k"));
+        assertEquals("b", tagged.get("k2"));
+        assertEquals("v", tagged2.get("k"));
+        assertEquals("b", tagged2.get("k2"));
+        assertEquals("x", tagged2.get("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#replaceTagsOf(org.openstreetmap.josm.data.osm.Tagged)}.
+     */
+    @Test
+    public void testReplaceTagsOfTagged() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC));
+        NodeData tagged = new NodeData();
+        tagged.put("k", "x");
+        tagged.put("k3", "x");
+        c.replaceTagsOf(tagged);
+        assertEquals("v", tagged.get("k"));
+        assertEquals("b", tagged.get("k2"));
+        assertEquals(null, tagged.get("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#replaceTagsOf(java.util.Collection)}.
+     */
+    @Test
+    public void testReplaceTagsOfCollectionOfQextendsTagged() {
+        TagCollection c = new TagCollection(Arrays.asList(tagA, tagC));
+        NodeData tagged = new NodeData();
+        NodeData tagged2 = new NodeData();
+        tagged2.put("k", "x");
+        tagged2.put("k3", "x");
+        c.replaceTagsOf(Arrays.asList(tagged, tagged2));
+        assertEquals("v", tagged.get("k"));
+        assertEquals("b", tagged.get("k2"));
+        assertEquals("v", tagged2.get("k"));
+        assertEquals("b", tagged2.get("k2"));
+        assertEquals(null, tagged2.get("k3"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#intersect(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testIntersect() {
+        TagCollection c1 = new TagCollection(Arrays.asList(tagA, tagC, tagD, tagEmpty));
+        TagCollection c2 = new TagCollection(Arrays.asList(tagA, tagB, tagD));
+        TagCollection c = c1.intersect(c2);
+        assertEquals(2, c.getKeys().size());
+        assertEquals(1, c.getTagOccurence(tagA));
+        assertEquals(1, c.getTagOccurence(tagD));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#minus(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testMinus() {
+        TagCollection c1 = new TagCollection(Arrays.asList(tagA, tagC, tagD, tagEmpty));
+        TagCollection c2 = new TagCollection(Arrays.asList(tagA, tagB, tagD));
+        TagCollection c = c1.minus(c2);
+        assertEquals(2, c.getKeys().size());
+        assertEquals(1, c.getTagOccurence(tagC));
+        assertEquals(1, c.getTagOccurence(tagEmpty));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#union(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testUnion() {
+        TagCollection c1 = new TagCollection(Arrays.asList(tagA, tagC, tagD, tagEmpty));
+        TagCollection c2 = new TagCollection(Arrays.asList(tagA, tagB, tagD));
+        TagCollection c = c1.union(c2);
+        assertEquals(2, c.getTagOccurence(tagA));
+        assertEquals(1, c.getTagOccurence(tagB));
+        assertEquals(1, c.getTagOccurence(tagC));
+        assertEquals(2, c.getTagOccurence(tagD));
+        assertEquals(1, c.getTagOccurence(tagEmpty));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#emptyTagsForKeysMissingIn(org.openstreetmap.josm.data.osm.TagCollection)}.
+     */
+    @Test
+    public void testEmptyTagsForKeysMissingIn() {
+        TagCollection c1 = new TagCollection(Arrays.asList(tagA, tagC, tagD, tagEmpty));
+        TagCollection c2 = new TagCollection(Arrays.asList(tagA, tagB, tagD));
+        TagCollection c = c1.emptyTagsForKeysMissingIn(c2);
+        assertEquals(2, c.getKeys().size());
+        assertEquals(1, c.getTagOccurence(new Tag(tagC.getKey(), "")));
+        assertEquals(1, c.getTagOccurence(tagEmpty));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getJoinedValues(java.lang.String)}.
+     */
+    @Test
+    public void testGetJoinedValues() {
+        TagCollection c = new TagCollection(Arrays.asList(new Tag("k", "a")));
+        assertEquals("a", c.getJoinedValues("k"));
+        TagCollection d = new TagCollection(Arrays.asList(new Tag("k", "a"), new Tag("k", "b")));
+        assertEquals("a;b", d.getJoinedValues("k"));
+        TagCollection e = new TagCollection(Arrays.asList(new Tag("k", "b"), new Tag("k", "a"), new Tag("k", "b;a")));
+        assertEquals("b;a", e.getJoinedValues("k"));
+        TagCollection f = new TagCollection(Arrays.asList(new Tag("k", "b"), new Tag("k", "a"), new Tag("k", "b"), new Tag("k", "c"), new Tag("k", "d"), new Tag("k", "a;b;c;d")));
+        assertEquals("a;b;c;d", f.getJoinedValues("k"));
+        TagCollection g = new TagCollection(Arrays.asList(new Tag("k", "b"), new Tag("k", "a"), new Tag("k", "b"), new Tag("k", "c"), new Tag("k", "d")));
+        assertEquals("a;b;c;d", Stream.of(g.getJoinedValues("k").split(";")).sorted().collect(Collectors.joining(";")));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#getSummedValues(java.lang.String)}.
+     */
+    @Test
+    public void testGetSummedValues() {
+        TagCollection c = new TagCollection(Arrays.asList(new Tag("k", "10"), new Tag("k", "20")));
+        assertEquals("30", c.getSummedValues("k"));
+        TagCollection d = new TagCollection(Arrays.asList(new Tag("k", "10"), new Tag("k", "10")));
+        assertEquals("10", d.getSummedValues("k"));
+        TagCollection e = new TagCollection(Arrays.asList(new Tag("k", "10"), new Tag("k", "x")));
+        assertEquals("10", e.getSummedValues("k"));
+        TagCollection f = new TagCollection();
+        assertEquals("0", f.getSummedValues("k"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.data.osm.TagCollection#toString()}.
+     */
+    @Test
+    @Ignore("TODO")
+    public void testToString() {
+        fail("Not yet implemented");
+    }
+}
