diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
index a6806c4..f4eb50c 100644
--- a/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
+++ b/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
@@ -83,7 +83,7 @@ public abstract class Condition {
         EQ, NEQ, GREATER_OR_EQUAL, GREATER, LESS_OR_EQUAL, LESS,
         REGEX, NREGEX, ONE_OF, BEGINS_WITH, ENDS_WITH, CONTAINS;
 
-        private static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
+        public static final Set<Op> NEGATED_OPS = EnumSet.of(NEQ, NREGEX);
 
         public boolean eval(String testString, String prototypeString) {
             if (testString == null && !NEGATED_OPS.contains(this))
diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
index c6f062f..a6c75fc 100644
--- a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
+++ b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
@@ -12,13 +12,14 @@ 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;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.PriorityQueue;
 import java.util.Set;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -40,6 +41,10 @@ import org.openstreetmap.josm.gui.mappaint.StyleKeys;
 import org.openstreetmap.josm.gui.mappaint.StyleSetting;
 import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting;
 import org.openstreetmap.josm.gui.mappaint.StyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyCondition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyMatchType;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.KeyValueCondition;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Op;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.SimpleKeyValueCondition;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
@@ -131,24 +136,87 @@ public class MapCSSStyleSource extends StyleSource {
      * The order of rules gets mixed up by this and needs to be sorted later.
      */
     public static class MapCSSRuleIndex {
-        /* all rules for this index */
-        public final List<MapCSSRule> rules = new ArrayList<>();
+        private final class RuleCandidatesIterator implements Iterator<MapCSSRule> {
+            private final BitSet ruleCandidates;
+            private int next;
+
+            private RuleCandidatesIterator(BitSet ruleCandidates) {
+                this.ruleCandidates = ruleCandidates;
+                next = ruleCandidates.nextSetBit(0);
+            }
+
+            @Override
+            public boolean hasNext() {
+                return next >= 0;
+            }
+
+            @Override
+            public MapCSSRule next() {
+                MapCSSRule rule = rules.get(next);
+                next = ruleCandidates.nextSetBit(next + 1);
+                return rule;
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        /**
+         * This is a map of all rules that are only applied if the primitive has a given key (and possibly value)
+         */
+        private final static class MapCSSKeyRules {
+            BitSet generalRules = new BitSet();
+            HashMap<String, BitSet> specialRules = new HashMap<>();
+
+            public void addGeneral(int ruleIndex) {
+                generalRules.set(ruleIndex);
+                for (BitSet r : specialRules.values()) {
+                    r.set(ruleIndex);
+                }
+            }
+
+            public void addSpecial(String value, int ruleIndex) {
+                BitSet forValue = specialRules.get(value);
+                if (forValue == null) {
+                    forValue = new BitSet();
+                    forValue.or(generalRules);
+                    specialRules.put(value.intern(), forValue);
+                }
+                forValue.set(ruleIndex);
+            }
+
+            public BitSet get(String value) {
+                BitSet forValue = specialRules.get(value);
+                if (forValue != null) return forValue; else return generalRules;
+            }
+        }
+
+        /* all rules for this index. This is sorted on every #initIndex() */
+        private final List<MapCSSRule> rules = new ArrayList<>();
         /* tag based index */
-        public final Map<String,Map<String,Set<MapCSSRule>>> index = new HashMap<>();
+        private final Map<String, MapCSSKeyRules> index = new HashMap<>();
         /* rules without SimpleKeyValueCondition */
-        public final ArrayList<MapCSSRule> remaining = new ArrayList<>();
+        private final BitSet remaining = new BitSet();
 
+        /**
+         * Add a rule to this index. This needs to be called before {@link #initIndex()} is called.
+         * @param rule The rule to add.
+         */
         public void add(MapCSSRule rule) {
             rules.add(rule);
         }
 
         /**
          * Initialize the index.
-         *
+         * <p>
          * You must own the write lock of STYLE_SOURCE_LOCK when calling this method.
          */
         public void initIndex() {
-            for (MapCSSRule r: rules) {
+            Collections.sort(rules);
+            for (int ruleIndex = 0; ruleIndex < rules.size(); ruleIndex++) {
+                MapCSSRule r = rules.get(ruleIndex);
                 // find the rightmost selector, this must be a GeneralSelector
                 Selector selRightmost = r.selector;
                 while (selRightmost instanceof ChildOrParentSelector) {
@@ -156,55 +224,81 @@ public class MapCSSStyleSource extends StyleSource {
                 }
                 OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost;
                 if (s.conds == null) {
-                    remaining.add(r);
+                    remaining.set(ruleIndex);
                     continue;
                 }
-                List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds, SimpleKeyValueCondition.class));
-                if (sk.isEmpty()) {
-                    remaining.add(r);
-                    continue;
-                }
-                SimpleKeyValueCondition c = sk.get(sk.size() - 1);
-                Map<String,Set<MapCSSRule>> rulesWithMatchingKey = index.get(c.k);
-                if (rulesWithMatchingKey == null) {
-                    rulesWithMatchingKey = new HashMap<>();
-                    index.put(c.k, rulesWithMatchingKey);
+                List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds,
+                        SimpleKeyValueCondition.class));
+                if (!sk.isEmpty()) {
+                    SimpleKeyValueCondition c = sk.get(sk.size() - 1);
+                    getEntryInIndex(c.k).addSpecial(c.v, ruleIndex);
+                } else {
+                    String key = findAnyRequiredKey(s.conds);
+                    if (key != null) {
+                        getEntryInIndex(key).addGeneral(ruleIndex);
+                    } else {
+                        remaining.set(ruleIndex);
+                    }
                 }
-                Set<MapCSSRule> rulesWithMatchingKeyValue = rulesWithMatchingKey.get(c.v);
-                if (rulesWithMatchingKeyValue == null) {
-                    rulesWithMatchingKeyValue = new HashSet<>();
-                    rulesWithMatchingKey.put(c.v, rulesWithMatchingKeyValue);
+            }
+        }
+
+        private String findAnyRequiredKey(List<Condition> conds) {
+            String key = null;
+            for (Condition c : conds) {
+                if (c instanceof KeyCondition) {
+                    KeyCondition keyCondition = (KeyCondition) c;
+                    if (!keyCondition.negateResult && conditionRequiresKeyPresence(keyCondition.matchType)) {
+                        key = keyCondition.label;
+                    }
+                } else if (c instanceof KeyValueCondition) {
+                    KeyValueCondition keyValueCondition = (KeyValueCondition) c;
+                    if (!Op.NEGATED_OPS.contains(keyValueCondition)) {
+                        key = keyValueCondition.k;
+                    }
                 }
-                rulesWithMatchingKeyValue.add(r);
             }
-            Collections.sort(remaining);
+            return key;
+        }
+
+        private boolean conditionRequiresKeyPresence(KeyMatchType matchType) {
+            return matchType != KeyMatchType.REGEX;
+        }
+
+        private MapCSSKeyRules getEntryInIndex(String key) {
+            MapCSSKeyRules rulesWithMatchingKey = index.get(key);
+            if (rulesWithMatchingKey == null) {
+                rulesWithMatchingKey = new MapCSSKeyRules();
+                index.put(key.intern(), rulesWithMatchingKey);
+            }
+            return rulesWithMatchingKey;
         }
 
         /**
          * Get a subset of all rules that might match the primitive.
+         * <p>
+         * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.
          * @param osm the primitive to match
          * @return a Collection of rules that filters out most of the rules
          * that cannot match, based on the tags of the primitive
-         *
-         * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.
          */
-        public PriorityQueue<MapCSSRule> getRuleCandidates(OsmPrimitive osm) {
-            PriorityQueue<MapCSSRule> ruleCandidates = new PriorityQueue<>(remaining);
-            for (Map.Entry<String,String> e : osm.getKeys().entrySet()) {
-                Map<String,Set<MapCSSRule>> v = index.get(e.getKey());
+        public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) {
+            final BitSet ruleCandidates = new BitSet(rules.size());
+            ruleCandidates.or(remaining);
+
+            for (Map.Entry<String, String> e : osm.getKeys().entrySet()) {
+                MapCSSKeyRules v = index.get(e.getKey());
                 if (v != null) {
-                    Set<MapCSSRule> rs = v.get(e.getValue());
-                    if (rs != null)  {
-                        ruleCandidates.addAll(rs);
-                    }
+                    BitSet rs = v.get(e.getValue());
+                    ruleCandidates.or(rs);
                 }
             }
-            return ruleCandidates;
+            return new RuleCandidatesIterator(ruleCandidates);
         }
 
         /**
          * Clear the index.
-         *
+         * <p>
          * You must own the write lock STYLE_SOURCE_LOCK when calling this method.
          */
         public void clear() {
@@ -488,9 +582,9 @@ public class MapCSSStyleSource extends StyleSource {
         // last used index
         int lastDeclUsed = -1;
 
-        PriorityQueue<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm);
-        MapCSSRule r;
-        while ((r = candidates.poll()) != null) {
+        Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm);
+        while (candidates.hasNext()) {
+            MapCSSRule r = candidates.next();
             env.clearSelectorMatchingInformation();
             env.layer = null;
             String sub = env.layer = r.selector.getSubpart().getId(env);
@@ -503,7 +597,8 @@ public class MapCSSStyleSource extends StyleSource {
                     continue;
                 }
 
-                if (r.declaration.idx == lastDeclUsed) continue; // don't apply one declaration more than once
+                if (r.declaration.idx == lastDeclUsed)
+                    continue; // don't apply one declaration more than once
                 lastDeclUsed = r.declaration.idx;
                 if ("*".equals(sub)) {
                     for (Entry<String, Cascade> entry : mc.getLayers()) {
