Index: src/java/org/apache/lucene/index/TermInfosReader.java
===================================================================
--- src/java/org/apache/lucene/index/TermInfosReader.java	(revision 630584)
+++ src/java/org/apache/lucene/index/TermInfosReader.java	(working copy)
@@ -18,15 +18,51 @@
  */
 
 import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
+import org.apache.lucene.store.BufferedIndexInput;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.BufferedIndexInput;
 
 /** This stores a monotonically increasing set of <Term, TermInfo> pairs in a
  * Directory.  Pairs are accessed either by Term or by ordinal position the
  * set.  */
 
 final class TermInfosReader {
+  /**
+   * Simple LRU cache that uses a bounded LinkedHashMap.
+   * This {@link #put(Term, TermInfo)} and {@link #get(Term)} methods
+   * are not synchronized for performance reasons. It should be very rare
+   * that multiple threads lookup the same term at exactly the same time.
+   * In that case we might get an unnecessary cache miss. 
+   */
+  private static final class LRUCache {
+    private final static float LOADFACTOR = 0.75f;
+    
+    private int cacheSize;
+    private Map map;
+    
+    LRUCache(int cacheSize) {
+      this.cacheSize = cacheSize;
+      int capacity = (int)Math.ceil(cacheSize / LOADFACTOR) + 1;
+
+      map = new LinkedHashMap(capacity, LOADFACTOR, true) {
+        protected boolean removeEldestEntry (Map.Entry eldest) {
+           return size() > LRUCache.this.cacheSize; 
+        }
+      }; 
+    }
+    
+    void put(Term term, TermInfo ti) {
+      map.put(term, ti);
+    }
+    
+    TermInfo get(Term term) {
+      return (TermInfo) map.get(term);
+    }
+  }
+
+  
   private Directory directory;
   private String segment;
   private FieldInfos fieldInfos;
@@ -44,6 +80,9 @@
   private int indexDivisor = 1;
   private int totalIndexInterval;
 
+  private LRUCache cache;
+  private static final int DEFAULT_CACHE_SIZE = 1024;
+  
   TermInfosReader(Directory dir, String seg, FieldInfos fis)
        throws CorruptIndexException, IOException {
     this(dir, seg, fis, BufferedIndexInput.BUFFER_SIZE);
@@ -53,6 +92,8 @@
        throws CorruptIndexException, IOException {
     boolean success = false;
 
+    cache = new LRUCache(DEFAULT_CACHE_SIZE);
+    
     try {
       directory = dir;
       segment = seg;
@@ -197,10 +238,23 @@
 
   /** Returns the TermInfo for a Term in the set, or null. */
   TermInfo get(Term term) throws IOException {
+    return get(term, true);
+  }
+  
+  /** Returns the TermInfo for a Term in the set, or null. */
+  private TermInfo get(Term term, boolean useCache) throws IOException {
     if (size == 0) return null;
-
     ensureIndexIsRead();
 
+    TermInfo ti;
+    // check the cache first if the term was recently looked up
+    if (useCache) {
+      ti = cache.get(term);
+      if (ti != null) {
+        return ti;
+      }
+    }
+    
     // optimize sequential access: first try scanning cached enum w/o seeking
     SegmentTermEnum enumerator = getEnum();
     if (enumerator.term() != null                 // term is at or past current
@@ -208,13 +262,18 @@
 	    || term.compareTo(enumerator.term()) >= 0)) {
       int enumOffset = (int)(enumerator.position/totalIndexInterval)+1;
       if (indexTerms.length == enumOffset	  // but before end of block
-	  || term.compareTo(indexTerms[enumOffset]) < 0)
-	return scanEnum(term);			  // no need to seek
+	    || term.compareTo(indexTerms[enumOffset]) < 0) {
+          ti = scanEnum(term);			  // no need to seek
+          cache.put(term, ti);
+          return ti;
+      }
     }
 
     // random-access: must seek
     seekEnum(getIndexOffset(term));
-    return scanEnum(term);
+    ti = scanEnum(term);
+    cache.put(term, ti);
+    return ti;
   }
 
   /** Scans within block for matching term. */
@@ -274,7 +333,9 @@
 
   /** Returns an enumeration of terms starting at or after the named term. */
   public SegmentTermEnum terms(Term term) throws IOException {
-    get(term);
+    // don't use the cache in this call because we want to reposition the
+    // enumeration
+    get(term, false);
     return (SegmentTermEnum)getEnum().clone();
   }
 }
