You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2019/08/23 09:27:23 UTC

[maven] 01/01: [MNG-6695] Improve speed in collection merging

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch MNG-6695
in repository https://gitbox.apache.org/repos/asf/maven.git

commit a52e2ea44a23e1f42f6e3b1de83208abaf4d2753
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Jun 28 23:36:05 2019 +0200

    [MNG-6695] Improve speed in collection merging
    
    This closes #262
---
 .../org/apache/maven/model/merge/ModelMerger.java  | 866 ++++++++++-----------
 1 file changed, 423 insertions(+), 443 deletions(-)

diff --git a/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java b/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java
index 8b4e0cd..dbd548b 100644
--- a/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java
+++ b/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java
@@ -19,8 +19,11 @@ package org.apache.maven.model.merge;
  * under the License.
  */
 
+import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -323,113 +326,29 @@ public class ModelMerger
     protected void mergeModel_Licenses( Model target, Model source, boolean sourceDominant,
                                         Map<Object, Object> context )
     {
-        List<License> src = source.getLicenses();
-        if ( !src.isEmpty() )
-        {
-            List<License> tgt = target.getLicenses();
-            Map<Object, License> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( License element : tgt )
-            {
-                Object key = getLicenseKey( element );
-                merged.put( key, element );
-            }
-
-            for ( License element : src )
-            {
-                Object key = getLicenseKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setLicenses( new ArrayList<>( merged.values() ) );
-        }
+        target.setLicenses( merge( target.getLicenses(), source.getLicenses(),
+                                    sourceDominant, new LicenseKeyComputer() ) );
     }
 
     protected void mergeModel_MailingLists( Model target, Model source, boolean sourceDominant,
                                             Map<Object, Object> context )
     {
-        List<MailingList> src = source.getMailingLists();
-        if ( !src.isEmpty() )
-        {
-            List<MailingList> tgt = target.getMailingLists();
-            Map<Object, MailingList> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( MailingList element : tgt )
-            {
-                Object key = getMailingListKey( element );
-                merged.put( key, element );
-            }
-
-            for ( MailingList element : src )
-            {
-                Object key = getMailingListKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setMailingLists( new ArrayList<>( merged.values() ) );
-        }
+        target.setMailingLists( merge( target.getMailingLists(), source.getMailingLists(),
+                                       sourceDominant, new MailingListKeyComputer() ) );
     }
 
     protected void mergeModel_Developers( Model target, Model source, boolean sourceDominant,
                                           Map<Object, Object> context )
     {
-        List<Developer> src = source.getDevelopers();
-        if ( !src.isEmpty() )
-        {
-            List<Developer> tgt = target.getDevelopers();
-            Map<Object, Developer> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Developer element : tgt )
-            {
-                Object key = getDeveloperKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Developer element : src )
-            {
-                Object key = getDeveloperKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setDevelopers( new ArrayList<>( merged.values() ) );
-        }
+        target.setDevelopers( merge( target.getDevelopers(), source.getDevelopers(),
+                sourceDominant, new DeveloperKeyComputer() ) );
     }
 
     protected void mergeModel_Contributors( Model target, Model source, boolean sourceDominant,
                                             Map<Object, Object> context )
     {
-        List<Contributor> src = source.getContributors();
-        if ( !src.isEmpty() )
-        {
-            List<Contributor> tgt = target.getContributors();
-            Map<Object, Contributor> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Contributor element : tgt )
-            {
-                Object key = getContributorKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Contributor element : src )
-            {
-                Object key = getContributorKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setContributors( new ArrayList<>( merged.values() ) );
-        }
+        target.setContributors( merge( target.getContributors(), source.getContributors(),
+                sourceDominant, new ContributorKeyComputer() ) );
     }
 
     protected void mergeModel_IssueManagement( Model target, Model source, boolean sourceDominant,
@@ -515,29 +434,8 @@ public class ModelMerger
     protected void mergeModel_Profiles( Model target, Model source, boolean sourceDominant,
                                         Map<Object, Object> context )
     {
-        List<Profile> src = source.getProfiles();
-        if ( !src.isEmpty() )
-        {
-            List<Profile> tgt = target.getProfiles();
-            Map<Object, Profile> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Profile element : tgt )
-            {
-                Object key = getProfileKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Profile element : src )
-            {
-                Object key = getProfileKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setProfiles( new ArrayList<>( merged.values() ) );
-        }
+        target.setProfiles( merge( target.getProfiles(), source.getProfiles(),
+                sourceDominant, new ProfileKeyComputer() ) );
     }
 
     protected void mergeModelBase( ModelBase target, ModelBase source, boolean sourceDominant,
@@ -570,85 +468,22 @@ public class ModelMerger
     protected void mergeModelBase_Dependencies( ModelBase target, ModelBase source, boolean sourceDominant,
                                                 Map<Object, Object> context )
     {
-        List<Dependency> src = source.getDependencies();
-        if ( !src.isEmpty() )
-        {
-            List<Dependency> tgt = target.getDependencies();
-            Map<Object, Dependency> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Dependency element : tgt )
-            {
-                Object key = getDependencyKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Dependency element : src )
-            {
-                Object key = getDependencyKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setDependencies( new ArrayList<>( merged.values() ) );
-        }
+        target.setDependencies( merge( target.getDependencies(), source.getDependencies(),
+                sourceDominant, new DependencyKeyComputer() ) );
     }
 
     protected void mergeModelBase_Repositories( ModelBase target, ModelBase source, boolean sourceDominant,
                                                 Map<Object, Object> context )
     {
-        List<Repository> src = source.getRepositories();
-        if ( !src.isEmpty() )
-        {
-            List<Repository> tgt = target.getRepositories();
-            Map<Object, Repository> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Repository element : tgt )
-            {
-                Object key = getRepositoryKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Repository element : src )
-            {
-                Object key = getRepositoryKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setRepositories( new ArrayList<>( merged.values() ) );
-        }
+        target.setRepositories( merge( target.getRepositories(), source.getRepositories(),
+                sourceDominant, new RepositoryKeyComputer() ) );
     }
 
     protected void mergeModelBase_PluginRepositories( ModelBase target, ModelBase source, boolean sourceDominant,
                                                       Map<Object, Object> context )
     {
-        List<Repository> src = source.getPluginRepositories();
-        if ( !src.isEmpty() )
-        {
-            List<Repository> tgt = target.getPluginRepositories();
-            Map<Object, Repository> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Repository element : tgt )
-            {
-                Object key = getRepositoryKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Repository element : src )
-            {
-                Object key = getRepositoryKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setPluginRepositories( new ArrayList<>( merged.values() ) );
-        }
+        target.setPluginRepositories( merge( target.getPluginRepositories(), source.getPluginRepositories(),
+                sourceDominant, new RepositoryKeyComputer() ) );
     }
 
     protected void mergeModelBase_DistributionManagement( ModelBase target, ModelBase source, boolean sourceDominant,
@@ -1236,30 +1071,8 @@ public class ModelMerger
     protected void mergeDependency_Exclusions( Dependency target, Dependency source, boolean sourceDominant,
                                                Map<Object, Object> context )
     {
-        List<Exclusion> src = source.getExclusions();
-        if ( !src.isEmpty() )
-        {
-            List<Exclusion> tgt = target.getExclusions();
-
-            Map<Object, Exclusion> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Exclusion element : tgt )
-            {
-                Object key = getExclusionKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Exclusion element : src )
-            {
-                Object key = getExclusionKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setExclusions( new ArrayList<>( merged.values() ) );
-        }
+        target.setExclusions( merge( target.getExclusions(), source.getExclusions(),
+                sourceDominant, new ExclusionKeyComputer() ) );
     }
 
     protected void mergeExclusion( Exclusion target, Exclusion source, boolean sourceDominant,
@@ -1336,30 +1149,8 @@ public class ModelMerger
     protected void mergeReporting_Plugins( Reporting target, Reporting source, boolean sourceDominant,
                                            Map<Object, Object> context )
     {
-        List<ReportPlugin> src = source.getPlugins();
-        if ( !src.isEmpty() )
-        {
-            List<ReportPlugin> tgt = target.getPlugins();
-            Map<Object, ReportPlugin> merged =
-                new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( ReportPlugin element : tgt )
-            {
-                Object key = getReportPluginKey( element );
-                merged.put( key, element );
-            }
-
-            for ( ReportPlugin element : src )
-            {
-                Object key = getReportPluginKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setPlugins( new ArrayList<>( merged.values() ) );
-        }
+        target.setPlugins( merge( target.getPlugins(), source.getPlugins(),
+                sourceDominant, new ReportPluginKeyComputer() ) );
     }
 
     protected void mergeReportPlugin( ReportPlugin target, ReportPlugin source, boolean sourceDominant,
@@ -1417,29 +1208,8 @@ public class ModelMerger
     protected void mergeReportPlugin_ReportSets( ReportPlugin target, ReportPlugin source, boolean sourceDominant,
                                                  Map<Object, Object> context )
     {
-        List<ReportSet> src = source.getReportSets();
-        if ( !src.isEmpty() )
-        {
-            List<ReportSet> tgt = target.getReportSets();
-            Map<Object, ReportSet> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( ReportSet element : tgt )
-            {
-                Object key = getReportSetKey( element );
-                merged.put( key, element );
-            }
-
-            for ( ReportSet element : src )
-            {
-                Object key = getReportSetKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setReportSets( new ArrayList<>( merged.values() ) );
-        }
+        target.setReportSets( merge( target.getReportSets(), source.getReportSets(),
+                sourceDominant, new ReportSetKeyComputer() ) );
     }
 
     protected void mergeReportSet( ReportSet target, ReportSet source, boolean sourceDominant,
@@ -1505,29 +1275,8 @@ public class ModelMerger
     protected void mergeDependencyManagement_Dependencies( DependencyManagement target, DependencyManagement source,
                                                            boolean sourceDominant, Map<Object, Object> context )
     {
-        List<Dependency> src = source.getDependencies();
-        if ( !src.isEmpty() )
-        {
-            List<Dependency> tgt = target.getDependencies();
-            Map<Object, Dependency> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Dependency element : tgt )
-            {
-                Object key = getDependencyKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Dependency element : src )
-            {
-                Object key = getDependencyKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setDependencies( new ArrayList<>( merged.values() ) );
-        }
+        target.setDependencies( merge( target.getDependencies(), source.getDependencies(),
+                                       sourceDominant, new DependencyKeyComputer() ) );
     }
 
     protected void mergeParent( Parent target, Parent source, boolean sourceDominant, Map<Object, Object> context )
@@ -2122,29 +1871,8 @@ public class ModelMerger
     protected void mergeCiManagement_Notifiers( CiManagement target, CiManagement source, boolean sourceDominant,
                                                 Map<Object, Object> context )
     {
-        List<Notifier> src = source.getNotifiers();
-        if ( !src.isEmpty() )
-        {
-            List<Notifier> tgt = target.getNotifiers();
-            Map<Object, Notifier> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Notifier element : tgt )
-            {
-                Object key = getNotifierKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Notifier element : src )
-            {
-                Object key = getNotifierKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setNotifiers( new ArrayList<>( merged.values() ) );
-        }
+        target.setNotifiers( merge( target.getNotifiers(), source.getNotifiers(),
+                                    sourceDominant, new NotifierKeyComputer() ) );
     }
 
     protected void mergeNotifier( Notifier target, Notifier source, boolean sourceDominant,
@@ -2342,29 +2070,8 @@ public class ModelMerger
     protected void mergeBuild_Extensions( Build target, Build source, boolean sourceDominant,
                                           Map<Object, Object> context )
     {
-        List<Extension> src = source.getExtensions();
-        if ( !src.isEmpty() )
-        {
-            List<Extension> tgt = target.getExtensions();
-            Map<Object, Extension> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Extension element : tgt )
-            {
-                Object key = getExtensionKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Extension element : src )
-            {
-                Object key = getExtensionKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setExtensions( new ArrayList<>( merged.values() ) );
-        }
+        target.setExtensions( merge( target.getExtensions(), source.getExtensions(),
+                                     sourceDominant, new ExtensionKeyComputer() ) );
     }
 
     protected void mergeExtension( Extension target, Extension source, boolean sourceDominant,
@@ -2488,57 +2195,15 @@ public class ModelMerger
     protected void mergeBuildBase_Resources( BuildBase target, BuildBase source, boolean sourceDominant,
                                              Map<Object, Object> context )
     {
-        List<Resource> src = source.getResources();
-        if ( !src.isEmpty() )
-        {
-            List<Resource> tgt = target.getResources();
-            Map<Object, Resource> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Resource element : tgt )
-            {
-                Object key = getResourceKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Resource element : src )
-            {
-                Object key = getResourceKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setResources( new ArrayList<>( merged.values() ) );
-        }
+        target.setResources( merge( target.getResources(), source.getResources(),
+                                    sourceDominant, new ResourceKeyComputer() ) );
     }
 
     protected void mergeBuildBase_TestResources( BuildBase target, BuildBase source, boolean sourceDominant,
                                                  Map<Object, Object> context )
     {
-        List<Resource> src = source.getTestResources();
-        if ( !src.isEmpty() )
-        {
-            List<Resource> tgt = target.getTestResources();
-            Map<Object, Resource> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Resource element : tgt )
-            {
-                Object key = getResourceKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Resource element : src )
-            {
-                Object key = getResourceKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setTestResources( new ArrayList<>( merged.values() ) );
-        }
+        target.setTestResources( merge( target.getTestResources(), source.getTestResources(),
+                                        sourceDominant, new ResourceKeyComputer() ) );
     }
 
     protected void mergePluginConfiguration( PluginConfiguration target, PluginConfiguration source,
@@ -2564,8 +2229,8 @@ public class ModelMerger
         }
     }
 
-    protected void mergePluginContainer( PluginContainer target, PluginContainer source, boolean sourceDominant,
-                                         Map<Object, Object> context )
+    protected void mergePluginContainer( PluginContainer target, PluginContainer source,
+                                         boolean sourceDominant, Map<Object, Object> context )
     {
         mergePluginContainer_Plugins( target, source, sourceDominant, context );
     }
@@ -2573,29 +2238,8 @@ public class ModelMerger
     protected void mergePluginContainer_Plugins( PluginContainer target, PluginContainer source,
                                                  boolean sourceDominant, Map<Object, Object> context )
     {
-        List<Plugin> src = source.getPlugins();
-        if ( !src.isEmpty() )
-        {
-            List<Plugin> tgt = target.getPlugins();
-            Map<Object, Plugin> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Plugin element : tgt )
-            {
-                Object key = getPluginKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Plugin element : src )
-            {
-                Object key = getPluginKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setPlugins( new ArrayList<>( merged.values() ) );
-        }
+        target.setPlugins( merge( target.getPlugins(), source.getPlugins(),
+                                  sourceDominant, new PluginKeyComputer() ) );
     }
 
     protected void mergePluginManagement( PluginManagement target, PluginManagement source, boolean sourceDominant,
@@ -2674,59 +2318,15 @@ public class ModelMerger
     protected void mergePlugin_Dependencies( Plugin target, Plugin source, boolean sourceDominant,
                                              Map<Object, Object> context )
     {
-        List<Dependency> src = source.getDependencies();
-        if ( !src.isEmpty() )
-        {
-            List<Dependency> tgt = target.getDependencies();
-            Map<Object, Dependency> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( Dependency element : tgt )
-            {
-                Object key = getDependencyKey( element );
-                merged.put( key, element );
-            }
-
-            for ( Dependency element : src )
-            {
-                Object key = getDependencyKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setDependencies( new ArrayList<>( merged.values() ) );
-        }
+        target.setDependencies( merge( target.getDependencies(), source.getDependencies(),
+                                       sourceDominant, new DependencyKeyComputer() ) );
     }
 
     protected void mergePlugin_Executions( Plugin target, Plugin source, boolean sourceDominant,
                                            Map<Object, Object> context )
     {
-        List<PluginExecution> src = source.getExecutions();
-        if ( !src.isEmpty() )
-        {
-            List<PluginExecution> tgt = target.getExecutions();
-
-            Map<Object, PluginExecution> merged =
-                new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
-
-            for ( PluginExecution element : tgt )
-            {
-                Object key = getPluginExecutionKey( element );
-                merged.put( key, element );
-            }
-
-            for ( PluginExecution element : src )
-            {
-                Object key = getPluginExecutionKey( element );
-                if ( sourceDominant || !merged.containsKey( key ) )
-                {
-                    merged.put( key, element );
-                }
-            }
-
-            target.setExecutions( new ArrayList<>( merged.values() ) );
-        }
+        target.setExecutions( merge( target.getExecutions(), source.getExecutions(),
+                                     sourceDominant, new ExecutionKeyComputer() ) );
     }
 
     protected void mergeConfigurationContainer( ConfigurationContainer target, ConfigurationContainer source,
@@ -3018,4 +2618,384 @@ public class ModelMerger
         return exclusion;
     }
 
+    /**
+     * Use to compute keys for data structures
+     * @param <T>
+     */
+    private interface KeyComputer<T>
+    {
+        Object key( T t );
+    }
+
+    /**
+     * Remapping function
+     * @param <T>
+     */
+    private interface Remapping<T>
+    {
+        T merge( T u, T v );
+    }
+
+    /**
+     * KeyComputer for Dependency
+     */
+    private final class DependencyKeyComputer implements KeyComputer<Dependency>
+    {
+        @Override
+        public Object key( Dependency dependency )
+        {
+            return getDependencyKey( dependency );
+        }
+    }
+
+    /**
+     * KeyComputer for License
+     */
+    private class LicenseKeyComputer implements KeyComputer<License>
+    {
+        @Override
+        public Object key( License license )
+        {
+            return getLicenseKey( license );
+        }
+    }
+
+    /**
+     * KeyComputer for MailingList
+     */
+    private class MailingListKeyComputer implements KeyComputer<MailingList>
+    {
+        @Override
+        public Object key( MailingList mailingList )
+        {
+            return getMailingListKey( mailingList );
+        }
+    }
+
+    /**
+     * KeyComputer for Developer
+     */
+    private class DeveloperKeyComputer implements KeyComputer<Developer>
+    {
+        @Override
+        public Object key( Developer developer )
+        {
+            return getDeveloperKey( developer );
+        }
+    }
+
+    /**
+     * KeyComputer for Contributor
+     */
+    private class ContributorKeyComputer implements KeyComputer<Contributor>
+    {
+        @Override
+        public Object key( Contributor contributor )
+        {
+            return getContributorKey( contributor );
+        }
+    }
+
+    /**
+     * KeyComputer for Profile
+     */
+    private class ProfileKeyComputer implements KeyComputer<Profile>
+    {
+        @Override
+        public Object key( Profile profile )
+        {
+            return getProfileKey( profile );
+        }
+    }
+
+    /**
+     * KeyComputer for Repository
+     */
+    private class RepositoryKeyComputer implements KeyComputer<Repository>
+    {
+        @Override
+        public Object key( Repository repository )
+        {
+            return getRepositoryKey( repository );
+        }
+    }
+
+    /**
+     * KeyComputer for ReportPlugin
+     */
+    private class ReportPluginKeyComputer implements KeyComputer<ReportPlugin>
+    {
+        @Override
+        public Object key( ReportPlugin plugin )
+        {
+            return getReportPluginKey( plugin );
+        }
+    }
+
+    /**
+     * KeyComputer for Plugin
+     */
+    private class PluginKeyComputer implements KeyComputer<Plugin>
+    {
+        @Override
+        public Object key( Plugin plugin )
+        {
+            return getPluginKey( plugin );
+        }
+    }
+
+    /**
+     * KeyComputer for ReportSet
+     */
+    private class ReportSetKeyComputer implements KeyComputer<ReportSet>
+    {
+        @Override
+        public Object key( ReportSet reportSet )
+        {
+            return getReportSetKey( reportSet );
+        }
+    }
+
+    /**
+     * KeyComputer for Notifier
+     */
+    private class NotifierKeyComputer implements KeyComputer<Notifier>
+    {
+        @Override
+        public Object key( Notifier notifier )
+        {
+            return getNotifierKey( notifier );
+        }
+    }
+
+    /**
+     * KeyComputer for Extension
+     */
+    private class ExtensionKeyComputer implements KeyComputer<Extension>
+    {
+        @Override
+        public Object key( Extension extension )
+        {
+            return getExtensionKey( extension );
+        }
+    }
+
+    /**
+     * KeyComputer for Resource
+     */
+    private class ResourceKeyComputer implements KeyComputer<Resource>
+    {
+        @Override
+        public Object key( Resource resource )
+        {
+            return getResourceKey( resource );
+        }
+    }
+
+    /**
+     * KeyComputer for PluginExecution
+     */
+    private class ExecutionKeyComputer implements KeyComputer<PluginExecution>
+    {
+        @Override
+        public Object key( PluginExecution pluginExecution )
+        {
+            return getPluginExecutionKey( pluginExecution );
+        }
+    }
+
+    /**
+     * KeyComputer for Exclusion
+     */
+    private class ExclusionKeyComputer implements KeyComputer<Exclusion>
+    {
+        @Override
+        public Object key( Exclusion exclusion )
+        {
+            return getExclusionKey( exclusion );
+        }
+    }
+
+    /**
+     * Return the second value if <code>sourceDominant</code> is true, the first one otherwise.
+     * @param <T>
+     */
+    private static class SourceDominant<T> implements Remapping<T>
+    {
+        private final boolean sourceDominant;
+
+        SourceDominant( boolean sourceDominant )
+        {
+            this.sourceDominant = sourceDominant;
+        }
+
+        @Override
+        public T merge( T u, T v )
+        {
+            return sourceDominant ? v : u;
+        }
+    }
+
+    /**
+     * Merge two lists
+     */
+    private static <T> List<T> merge( List<T> tgt, List<T> src, boolean sourceDominant, KeyComputer<T> computer )
+    {
+        return merge( tgt, src, computer, new SourceDominant<T>( sourceDominant ) );
+    }
+
+    private static <T> List<T> merge( List<T> tgt, List<T> src, KeyComputer<T> computer, Remapping<T> remapping )
+    {
+        if ( src.isEmpty() )
+        {
+            return tgt;
+        }
+
+        MergingList<T> list;
+        if ( tgt instanceof MergingList )
+        {
+            list = (MergingList<T>) tgt;
+        }
+        else
+        {
+            list = new MergingList<>( computer, src.size() + tgt.size() );
+            list.mergeAll( tgt, new SourceDominant<T>( true ) );
+        }
+
+        list.mergeAll( src, remapping );
+        return list;
+    }
+
+    /**
+     * Merging list
+     * @param <V>
+     */
+    private static class MergingList<V> extends AbstractList<V>
+    {
+        private final KeyComputer<V> keyComputer;
+        private Map<Object, V> map;
+        private List<V> list;
+
+        MergingList( KeyComputer<V> keyComputer, int initialCapacity )
+        {
+            this.map = new LinkedHashMap<>( initialCapacity );
+            this.keyComputer = keyComputer;
+        }
+
+        @Override
+        public Iterator<V> iterator()
+        {
+            if ( map != null )
+            {
+                return map.values().iterator();
+            }
+            else
+            {
+                return list.iterator();
+            }
+        }
+
+        void mergeAll( Collection<V> vs, Remapping<V> remapping )
+        {
+            if ( map == null )
+            {
+                map = new LinkedHashMap<>( list.size() + vs.size() );
+                for ( V v : list )
+                {
+                    map.put( keyComputer.key( v ), v );
+                }
+                list = null;
+            }
+            if ( vs instanceof MergingList && ( (MergingList) vs ).map != null )
+            {
+                for ( Map.Entry<Object, V> e : ( (MergingList<V>) vs ).map.entrySet() )
+                {
+                    Object key = e.getKey();
+                    V oldValue = map.get( key );
+                    // JDK8: this should be a call to map.merge( key, v, remapping )
+                    V newValue = ( oldValue == null ) ? e.getValue() : remapping.merge( oldValue, e.getValue() );
+                    if ( newValue == null )
+                    {
+                        remove( key );
+                    }
+                    else if ( newValue != oldValue )
+                    {
+                        map.put( key, newValue );
+                    }
+                }
+            }
+            else
+            {
+                for ( V v : vs )
+                {
+                    Object key = keyComputer.key( v );
+                    // JDK8: this should be a call to map.merge( key, v, remapping )
+                    V oldValue = map.get( key );
+                    V newValue = ( oldValue == null ) ? v : remapping.merge( oldValue, v );
+                    if ( newValue == null )
+                    {
+                        remove( key );
+                    }
+                    else
+                    {
+                        map.put( key, newValue );
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean contains( Object o )
+        {
+            if ( map != null )
+            {
+                return map.containsValue( o );
+            }
+            else
+            {
+                return list.contains( o );
+            }
+        }
+
+        private List<V> asList()
+        {
+            if ( list == null )
+            {
+                list = new ArrayList<>( map.values() );
+                map = null;
+            }
+            return list;
+        }
+
+        @Override
+        public void add( int index, V element )
+        {
+            asList().add( index, element );
+        }
+
+        @Override
+        public V remove( int index )
+        {
+            return asList().remove( index );
+        }
+
+        @Override
+        public V get( int index )
+        {
+            return asList().get( index );
+        }
+
+        @Override
+        public int size()
+        {
+            if ( map != null )
+            {
+                return map.size();
+            }
+            else
+            {
+                return list.size();
+            }
+        }
+    }
 }