diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
index 6de6336..9d33cf1 100644
--- a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
+++ b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
@@ -12,7 +12,6 @@ import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -138,17 +137,134 @@ public class MapCSSStyleSource extends StyleSource {
      */
     public static class MapCSSRuleIndex {
         /**
-         * This is an iterator over all rules that are marked as possible in the bitset.
+         * This is a subset of the MapCSS rules. It is represented by storing the indexes in the rules array. This allows for fast merging.
+         *
+         * It alternatively uses a list of set indexes, to ensure O(<number of set bits>) space requirement.
+         *
+         * @author Michael Zangl
+         */
+        private final static class RuleBitset {
+            /**
+             * A (sorted) array of rule indexes. Those indexes are the allowed rules. We do not use an {@link ArrayList} here because this would require us to box all integers (slow).
+             */
+            private int[] ruleIndexes = new int[5];
+
+            /**
+             * Real length of {@link #ruleIndexes}
+             */
+            private int ruleIndexesLength = 0;
+
+            /**
+             * The bitset data. We always store whole words. If it is <code>null</code>, this is not used.
+             */
+            private long[] bitsetData;
+            /**
+             * The number of words after which bitsetData starts.
+             */
+            private int bitsetOffset;
+
+            private void addRule(int ruleIndex) {
+                if (bitsetData != null) {
+                    throw new IllegalStateException("Cannot add to optimized bitset.");
+                }
+                if (ruleIndexesLength > 0 && ruleIndexes[ruleIndexesLength - 1] >= ruleIndex) {
+                    throw new IllegalArgumentException("Rules must be sorted.");
+                }
+
+                if (ruleIndexesLength >= ruleIndexes.length) {
+                    int[] newIndexes = new int[ruleIndexes.length * 2];
+                    System.arraycopy(ruleIndexes, 0, newIndexes, 0, ruleIndexes.length);
+                    ruleIndexes = newIndexes;
+                }
+
+                ruleIndexes[ruleIndexesLength] = ruleIndex;
+                ruleIndexesLength++;
+            }
+
+            /**
+             * Optimize this list by converting it to a bitset (at least partially)
+             */
+            private void optimize() {
+                if (ruleIndexesLength == 0) {
+                    return;
+                }
+
+                int minRule = ruleIndexes[0];
+                int maxRule = ruleIndexes[ruleIndexesLength - 1];
+
+                // current policy: completely convert to bitset if on average 1 bit per word is set.
+                // alternatives: convert one dense area, ...
+                int usedWords = getWord(maxRule) - getWord(minRule) + 1;
+                if (usedWords <= ruleIndexesLength - 1) {
+                    bitsetOffset = getWord(minRule);
+                    bitsetData = new long[usedWords];
+                    for (int i = 0; i < ruleIndexesLength; i++) {
+                        int ruleIndex = ruleIndexes[i];
+                        bitsetData[getWord(ruleIndex) - bitsetOffset] |= getMask(ruleIndex);
+                    }
+                    ruleIndexes = null;
+                    ruleIndexesLength = 0;
+                }
+            }
+
+            private static long getMask(int bit) {
+                return (1l << (bit & 63));
+            }
+
+            private static int getWord(int bit) {
+                return bit / 64;
+            }
+
+            public void setBitsIn(long[] ruleCandidateBits) {
+                if (bitsetData != null) {
+                    for (int i = 0 ; i < bitsetData.length; i++) {
+                        ruleCandidateBits[bitsetOffset + i] |= bitsetData[i] ;
+                    }
+                }
+
+                for (int i = 0; i < ruleIndexesLength; i++) {
+                    int bitIndex = ruleIndexes[i];
+                    ruleCandidateBits[getWord(bitIndex)] |= getMask(bitIndex);
+                }
+            }
+        }
+
+        /**
+         * This is an iterator over all rules that are marked as possible in the bitsets. Rules can be added to it before it is used.
          *
          * @author Michael Zangl
          */
         private final class RuleCandidatesIterator implements Iterator<MapCSSRule> {
-            private final BitSet ruleCandidates;
+            /**
+             * The rules that this iterator should iterate over.
+             * <p>
+             * We cannot use a bitset here, because it does not expose it's long[]
+             */
+            private final long[] ruleCandidateBits;
             private int next;
 
-            private RuleCandidatesIterator(BitSet ruleCandidates) {
-                this.ruleCandidates = ruleCandidates;
-                next = ruleCandidates.nextSetBit(0);
+            private RuleCandidatesIterator(long[] ruleCandidateBits) {
+                this.ruleCandidateBits = ruleCandidateBits;
+                next = nextSetBit(0);
+            }
+
+            private int nextSetBit(int from) {
+                int wordIndex = RuleBitset.getWord(from);
+                if (wordIndex == ruleCandidateBits.length) {
+                    return -1;
+                }
+
+                long nextWord = ruleCandidateBits[wordIndex] & (-1l << from);
+
+                while (true) {
+                    if (nextWord != 0)
+                        return (wordIndex * 64) + Long.numberOfTrailingZeros(nextWord);
+
+                    wordIndex++;
+                    if (wordIndex == ruleCandidateBits.length)
+                        return -1;
+                    nextWord = ruleCandidateBits[wordIndex];
+                }
             }
 
             @Override
@@ -159,7 +275,7 @@ public class MapCSSStyleSource extends StyleSource {
             @Override
             public MapCSSRule next() {
                 MapCSSRule rule = rules.get(next);
-                next = ruleCandidates.nextSetBit(next + 1);
+                next = nextSetBit(next + 1);
                 return rule;
             }
 
@@ -167,6 +283,24 @@ public class MapCSSStyleSource extends StyleSource {
             public void remove() {
                 throw new UnsupportedOperationException();
             }
+
+            @Override
+            public String toString() {
+                StringBuilder builder = new StringBuilder();
+                builder.append("RuleCandidatesIterator [ruleCandidateBits=[");
+                boolean needComma = false;
+                for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
+                    if (needComma) {
+                        builder.append(", ");
+                    }
+                    needComma = true;
+                    builder.append(i);
+                }
+                builder.append("], next=");
+                builder.append(next);
+                builder.append("]");
+                return builder.toString();
+            }
         }
 
         /**
@@ -178,34 +312,39 @@ public class MapCSSStyleSource extends StyleSource {
             /**
              * The indexes of rules that might be applied if this tag is present and the value has no special handling.
              */
-            BitSet generalRules = new BitSet();
+            RuleBitset generalRules = new RuleBitset();
 
             /**
              * A map that sores the indexes of rules that might be applied if the key=value pair is present on this
              * primitive. This includes all key=* rules.
              */
-            Map<String, BitSet> specialRules = new HashMap<>();
+            HashMap<String, RuleBitset> specialRules = new HashMap<>();
 
             public void addForKey(int ruleIndex) {
-                generalRules.set(ruleIndex);
-                for (BitSet r : specialRules.values()) {
-                    r.set(ruleIndex);
-                }
+                generalRules.addRule(ruleIndex);
             }
 
             public void addForKeyAndValue(String value, int ruleIndex) {
-                BitSet forValue = specialRules.get(value);
+                RuleBitset forValue = specialRules.get(value);
                 if (forValue == null) {
-                    forValue = new BitSet();
-                    forValue.or(generalRules);
+                    forValue = new RuleBitset();
                     specialRules.put(value.intern(), forValue);
                 }
-                forValue.set(ruleIndex);
+                forValue.addRule(ruleIndex);
             }
 
-            public BitSet get(String value) {
-                BitSet forValue = specialRules.get(value);
-                if (forValue != null) return forValue; else return generalRules;
+            public void optimize() {
+                generalRules.optimize();
+                for (RuleBitset bitset : specialRules.values()) {
+                    bitset.optimize();
+                }
+            }
+
+            public void setBitsIn(long[] ruleCandidateBits, String value) {
+                RuleBitset forValue = specialRules.get(value);
+                if (forValue != null)
+                    forValue.setBitsIn(ruleCandidateBits);
+                generalRules.setBitsIn(ruleCandidateBits);
             }
         }
 
@@ -220,7 +359,7 @@ public class MapCSSStyleSource extends StyleSource {
         /**
          * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored.
          */
-        private final BitSet remaining = new BitSet();
+        private RuleBitset remaining = new RuleBitset();
 
         /**
          * Add a rule to this index. This needs to be called before {@link #initIndex()} is called.
@@ -246,7 +385,7 @@ public class MapCSSStyleSource extends StyleSource {
                 }
                 OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost;
                 if (s.conds == null) {
-                    remaining.set(ruleIndex);
+                    remaining.addRule(ruleIndex);
                     continue;
                 }
                 List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds,
@@ -259,10 +398,15 @@ public class MapCSSStyleSource extends StyleSource {
                     if (key != null) {
                         getEntryInIndex(key).addForKey(ruleIndex);
                     } else {
-                        remaining.set(ruleIndex);
+                        remaining.addRule(ruleIndex);
                     }
                 }
             }
+
+            remaining.optimize();
+            for (MapCSSKeyRules rules : index.values()) {
+                rules.optimize();
+            }
         }
 
         /**
@@ -312,17 +456,16 @@ public class MapCSSStyleSource extends StyleSource {
          * @return An iterator over possible rules in the right order.
          */
         public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) {
-            final BitSet ruleCandidates = new BitSet(rules.size());
-            ruleCandidates.or(remaining);
+            long[] ruleCandidateBits = new long[RuleBitset.getWord(rules.size() - 1) + 1];
+            remaining.setBitsIn(ruleCandidateBits);
 
             for (Map.Entry<String, String> e : osm.getKeys().entrySet()) {
                 MapCSSKeyRules v = index.get(e.getKey());
                 if (v != null) {
-                    BitSet rs = v.get(e.getValue());
-                    ruleCandidates.or(rs);
+                    v.setBitsIn(ruleCandidateBits, e.getValue());
                 }
             }
-            return new RuleCandidatesIterator(ruleCandidates);
+            return new RuleCandidatesIterator(ruleCandidateBits);
         }
 
         /**
@@ -333,7 +476,7 @@ public class MapCSSStyleSource extends StyleSource {
         public void clear() {
             rules.clear();
             index.clear();
-            remaining.clear();
+            remaining = new RuleBitset();
         }
     }
 
