Index: lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/surround/query/SrndQueryTest.java
===================================================================
--- lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/surround/query/SrndQueryTest.java	(revision 0)
+++ lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/surround/query/SrndQueryTest.java	(revision 0)
@@ -0,0 +1,40 @@
+package org.apache.lucene.queryParser.surround.query;
+
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.queryParser.surround.parser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryUtils;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+/**
+ *
+ *
+ **/
+public class SrndQueryTest extends LuceneTestCase {
+
+  @Test
+  public void testHashEquals() throws Exception {
+    SrndQuery sq = QueryParser.parse("word1 w word2");
+    BasicQueryFactory qf = new BasicQueryFactory(16);
+    Query lq = sq.makeLuceneQueryField("foo", qf);
+    QueryUtils.checkHashEquals(lq);
+  }
+}
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java	(revision 1078068)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java	(working copy)
@@ -39,6 +39,7 @@
     this.ordered = ordered;
   }
 
+
   private int opDistance;
   public int getOpDistance() {return opDistance;}
   
@@ -84,7 +85,16 @@
       public Query rewrite(IndexReader reader) throws IOException {
         return getSpanNearQuery(reader, fieldName, getBoost(), qf);
       }
-      
+
+      @Override
+      public int hashCode() {
+        return DistanceQuery.this.hashCode();
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+        return DistanceQuery.this.equals(obj);
+      }
     };
   }
   
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java	(revision 1078068)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java	(working copy)
@@ -39,6 +39,10 @@
   
   public abstract String toStringUnquoted();
   
+  /** @deprecated Not normally used, to be removed from Lucene 5.0.
+   *   This class implementing Comparable is to be removed at the same time.
+   */
+  @Deprecated
   public int compareTo(SimpleTerm ost) {
     /* for ordering terms and prefixes before using an index, not used */
     return this.toStringUnquoted().compareTo( ost.toStringUnquoted());
@@ -96,6 +100,16 @@
                   /* luceneSubQueries all have default weight */
                   luceneSubQueries, BooleanClause.Occur.SHOULD); /* OR the subquery terms */ 
       }
+
+      @Override
+      public int hashCode() {
+	return SimpleTerm.this.hashCode();
+      }
+    
+      @Override
+      public boolean equals(Object obj) {
+	return SimpleTerm.this.equals(obj);
+      }                                                     
     };
   }
     
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SrndQuery.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SrndQuery.java	(revision 1078068)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SrndQuery.java	(working copy)
@@ -53,6 +53,9 @@
   
   public abstract Query makeLuceneQueryFieldNoBoost(String fieldName, BasicQueryFactory qf);
   
+  /** This method is used by {@link #hashCode()} and {@link #equals(Object)},
+   *  see LUCENE-2945.
+   */
   @Override
   public abstract String toString();
   
@@ -66,8 +69,30 @@
       throw new Error(cns);
     }
   }
-  
-/* An empty Lucene query */
+
+  /** For subclasses of {@link SrndQuery} within the package
+   *  {@link org.apache.lucene.queryParser.surround.query}
+   *  it is not necessary to override this method.
+   */
+  @Override
+  public int hashCode() {
+    return getClass().hashCode() ^ toString().hashCode();
+  }
+
+  /** For subclasses of {@link SrndQuery} within the package
+   *  {@link org.apache.lucene.queryParser.surround.query}
+   *  it is not necessary to override this method.
+   */
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null)
+      return false;
+    if (! getClass().equals(obj.getClass()))
+      return false;
+    return toString().equals(obj.toString());
+  }
+
+  /** An empty Lucene query */
   public final static Query theEmptyLcnQuery = new BooleanQuery() { /* no changes allowed */
     @Override
     public void setBoost(float boost) {
