Optimize hasKeyword() to not need to allocate and concatenate a bunch of lists together.

Instead, makes it use the visitor pattern which allows traversing all the keywords without (usually) needing to allocate any  temporary lists.
This commit is contained in:
Myrd
2014-12-31 00:38:45 +00:00
parent 45c17d35da
commit 1f5cca02dd

View File

@@ -1194,7 +1194,7 @@ public class Card extends GameEntity implements Comparable<Card> {
// get the text that does not belong to a cards abilities (and is not really // get the text that does not belong to a cards abilities (and is not really
// there rules-wise) // there rules-wise)
public final String getNonAbilityText() { public final String getNonAbilityText() {
return keywordsToText(getHiddenExtrinsicKeyword()); return keywordsToText(getHiddenExtrinsicKeywords());
} }
// convert a keyword list to the String that should be displayed in game // convert a keyword list to the String that should be displayed in game
@@ -2742,9 +2742,13 @@ public class Card extends GameEntity implements Comparable<Card> {
return getKeywords(currentState); return getKeywords(currentState);
} }
public final List<String> getKeywords(CardState state) { public final List<String> getKeywords(CardState state) {
final ArrayList<String> keywords = getUnhiddenKeywords(state); ListKeywordVisitor visitor = new ListKeywordVisitor();
keywords.addAll(getHiddenExtrinsicKeyword()); visitKeywords(state, visitor);
return keywords; return visitor.getKeywords();
}
public final void visitKeywords(CardState state, KeywordVisitor visitor) {
visitUnhiddenKeywords(state, visitor);
visitHiddenExtreinsicKeywords(visitor);
} }
public final int getKeywordAmount(String keyword) { public final int getKeywordAmount(String keyword) {
@@ -2752,13 +2756,24 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public final int getKeywordAmount(CardState state, String keyword) { public final int getKeywordAmount(CardState state, String keyword) {
int res = 0; CountKeywordVisitor visitor = new CountKeywordVisitor(keyword);
for (final String k : getKeywords(state)) { visitKeywords(state, visitor);
if (k.equals(keyword)) { return visitor.getCount();
res++;
} }
@Override
public final boolean hasKeyword(String keyword) {
return hasKeyword(keyword, currentState);
} }
return res;
public final boolean hasKeyword(String keyword, CardState state) {
if (keyword.startsWith("HIDDEN")) {
keyword = keyword.substring(7);
}
CountKeywordVisitor visitor = new CountKeywordVisitor(keyword);
visitKeywords(state, visitor);
return visitor.getCount() > 0;
} }
public final void updateKeywords() { public final void updateKeywords() {
@@ -2832,6 +2847,21 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
return keywords; return keywords;
} }
private void visitUnhiddenKeywords(CardState state, KeywordVisitor visitor) {
if (changedCardKeywords.isEmpty()) {
// Fast path that doesn't involve temp allocations.
for (String kw : state.getIntrinsicKeywords()) {
visitor.visit(kw);
}
for (String kw : extrinsicKeyword) {
visitor.visit(kw);
}
} else {
for (String kw : getUnhiddenKeywords()) {
visitor.visit(kw);
}
}
}
/** /**
* Replace all instances of one color word in this card's text by another. * Replace all instances of one color word in this card's text by another.
@@ -3028,10 +3058,12 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
// Hidden Keywords will be returned without the indicator HIDDEN // Hidden Keywords will be returned without the indicator HIDDEN
public final ArrayList<String> getHiddenExtrinsicKeyword() { public final ArrayList<String> getHiddenExtrinsicKeywords() {
while (true) { ListKeywordVisitor visitor = new ListKeywordVisitor();
try { visitHiddenExtreinsicKeywords(visitor);
final ArrayList<String> keywords = new ArrayList<String>(); return visitor.getKeywords();
}
private void visitHiddenExtreinsicKeywords(KeywordVisitor visitor) {
for (String keyword : hiddenExtrinsicKeyword) { for (String keyword : hiddenExtrinsicKeyword) {
if (keyword == null) { if (keyword == null) {
continue; continue;
@@ -3039,12 +3071,7 @@ public class Card extends GameEntity implements Comparable<Card> {
if (keyword.startsWith("HIDDEN")) { if (keyword.startsWith("HIDDEN")) {
keyword = keyword.substring(7); keyword = keyword.substring(7);
} }
keywords.add(keyword); visitor.visit(keyword);
}
return keywords;
} catch (IndexOutOfBoundsException ex) {
// Do nothing and let the while loop retry
}
} }
} }
@@ -3300,17 +3327,6 @@ public class Card extends GameEntity implements Comparable<Card> {
return false; return false;
} }
@Override
public final boolean hasKeyword(String keyword) {
return hasKeyword(keyword, currentState);
}
public final boolean hasKeyword(String keyword, CardState state) {
if (keyword.startsWith("HIDDEN")) {
return getKeywords(state).contains(keyword.substring(7));
}
return getKeywords(state).contains(keyword);
}
public final boolean hasStartOfKeyword(final String keyword) { public final boolean hasStartOfKeyword(final String keyword) {
return hasStartOfKeyword(keyword, currentState); return hasStartOfKeyword(keyword, currentState);
} }
@@ -6380,4 +6396,46 @@ public class Card extends GameEntity implements Comparable<Card> {
public CardView getView() { public CardView getView() {
return view; return view;
} }
// Interface that allows traversing the card's keywords without needing to
// concat a bunch of lists. Optimizes common operations such as hasKeyword().
private interface KeywordVisitor {
public void visit(String keyword);
}
// Counts number of instances of a given keyword.
private static final class CountKeywordVisitor implements KeywordVisitor {
private String keyword;
private int count;
public CountKeywordVisitor(String keyword) {
this.keyword = keyword;
this.count = 0;
}
@Override
public void visit(String kw) {
if (kw.equals(keyword)) {
count++;
}
}
public int getCount() {
return count;
}
}
// Collects all the keywords into a list.
private static final class ListKeywordVisitor implements KeywordVisitor {
private ArrayList<String> keywords = new ArrayList<>();
@Override
public void visit(String kw) {
keywords.add(kw);
}
public ArrayList<String> getKeywords() {
return keywords;
}
}
} }