diff --git a/src/PlaylistTag.cxx b/src/PlaylistTag.cxx
index 5813ec1c13a3683beb63457dab93ca27e761cffd..e158018f5315ea204f72b6b5e6cb04890c59161f 100644
--- a/src/PlaylistTag.cxx
+++ b/src/PlaylistTag.cxx
@@ -28,6 +28,7 @@
 #include "PlaylistError.hxx"
 #include "Song.hxx"
 #include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
 #include "util/Error.hxx"
 
 bool
@@ -48,9 +49,15 @@ playlist::AddSongIdTag(unsigned id, TagType tag_type, const char *value,
 		return false;
 	}
 
-	if (song.tag == nullptr)
-		song.tag = new Tag();
-	song.tag->AddItem(tag_type, value);
+	TagBuilder tag;
+	if (song.tag != nullptr) {
+		tag = std::move(*song.tag);
+		delete song.tag;
+	}
+
+	tag.AddItem(tag_type, value);
+	song.tag = tag.Commit();
+
 	queue.ModifyAtPosition(position);
 	OnModified();
 	return true;
@@ -77,10 +84,15 @@ playlist::ClearSongIdTag(unsigned id, TagType tag_type,
 	if (song.tag == nullptr)
 		return true;
 
+	TagBuilder tag(std::move(*song.tag));
+	delete song.tag;
+
 	if (tag_type == TAG_NUM_OF_ITEM_TYPES)
-		song.tag->RemoveAll();
+		tag.RemoveAll();
 	else
-		song.tag->RemoveType(tag_type);
+		tag.RemoveType(tag_type);
+	song.tag = tag.Commit();
+
 	queue.ModifyAtPosition(position);
 	OnModified();
 	return true;
diff --git a/src/tag/Tag.cxx b/src/tag/Tag.cxx
index 7d1da63dfd76306e5c0f242963e2de5076f99cb3..43ded308aaab27b66fccd1972f2c6fa03e3a9653 100644
--- a/src/tag/Tag.cxx
+++ b/src/tag/Tag.cxx
@@ -187,23 +187,3 @@ Tag::AddItem(TagType type, const char *value)
 {
 	AddItem(type, value, strlen(value));
 }
-
-void
-Tag::RemoveType(TagType type)
-{
-	auto dest = items, src = items, end = items + num_items;
-
-	tag_pool_lock.lock();
-	while (src != end) {
-		TagItem *item = *src++;
-		if (item->type == type)
-			/* remove it */
-			tag_pool_put_item(item);
-		else
-			/* keep it */
-			*dest++ = item;
-	}
-	tag_pool_lock.unlock();
-
-	num_items = dest - items;
-}
diff --git a/src/tag/Tag.hxx b/src/tag/Tag.hxx
index 0e48298a753a5728097ad27ef10b4394aaa1853e..e404a4f2628798048750ff0b513251cb716605eb 100644
--- a/src/tag/Tag.hxx
+++ b/src/tag/Tag.hxx
@@ -121,18 +121,6 @@ struct Tag {
 	 */
 	void AddItem(TagType type, const char *value);
 
-	/**
-	 * Removes all tag items.
-	 */
-	void RemoveAll() {
-		num_items = 0;
-	}
-
-	/**
-	 * Removes all tag items of the specified type.
-	 */
-	void RemoveType(TagType type);
-
 	/**
 	 * Merges the data from two tags.  If both tags share data for the
 	 * same TagType, only data from "add" is used.
diff --git a/src/tag/TagBuilder.cxx b/src/tag/TagBuilder.cxx
index 2d3b49eb53b9315c6fe03dc7d5700559539d430e..5c7da2a1a0471dca60b23f77d0bebcc5ca96492d 100644
--- a/src/tag/TagBuilder.cxx
+++ b/src/tag/TagBuilder.cxx
@@ -108,13 +108,7 @@ TagBuilder::Clear()
 {
 	time = -1;
 	has_playlist = false;
-
-	tag_pool_lock.lock();
-	for (auto i : items)
-		tag_pool_put_item(i);
-	tag_pool_lock.unlock();
-
-	items.clear();
+	RemoveAll();
 }
 
 void
@@ -216,3 +210,29 @@ TagBuilder::AddItem(TagType type, const char *value)
 
 	AddItem(type, value, strlen(value));
 }
+
+void
+TagBuilder::RemoveAll()
+{
+	tag_pool_lock.lock();
+	for (auto i : items)
+		tag_pool_put_item(i);
+	tag_pool_lock.unlock();
+
+	items.clear();
+}
+
+void
+TagBuilder::RemoveType(TagType type)
+{
+	const auto begin = items.begin(), end = items.end();
+
+	items.erase(std::remove_if(begin, end,
+				   [type](TagItem *item) {
+					   if (item->type != type)
+						   return false;
+					   tag_pool_put_item(item);
+					   return true;
+				   }),
+		    end);
+}
diff --git a/src/tag/TagBuilder.hxx b/src/tag/TagBuilder.hxx
index cd4fa4e578b328c63dc7fb870777bfe6ff976aab..fe647db08b87e1c99cabbef37ab48919d971323e 100644
--- a/src/tag/TagBuilder.hxx
+++ b/src/tag/TagBuilder.hxx
@@ -147,6 +147,16 @@ public:
 	gcc_nonnull_all
 	void AddItem(TagType type, const char *value);
 
+	/**
+	 * Removes all tag items.
+	 */
+	void RemoveAll();
+
+	/**
+	 * Removes all tag items of the specified type.
+	 */
+	void RemoveType(TagType type);
+
 private:
 	gcc_nonnull_all
 	void AddItemInternal(TagType type, const char *value, size_t length);