搜索引擎(Lucene-搜索详解)

Lucene搜索流程详解

问题1:搜索的过程是怎样的?

回顾架构图

347761775733fd75ade3364a0a2f0d325a0.jpg

Lucene搜索代码示例

public class SearchBaseFlow {
    public static void main(String[] args) throws IOException, ParseException {
	// 使用的分词器
	Analyzer analyzer = new IKAnalyzer4Lucene7(true);
	// 索引存储目录
	Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
	// 索引读取器
	IndexReader indexReader = DirectoryReader.open(directory);
	// 索引搜索器
	IndexSearcher indexSearcher = new IndexSearcher(indexReader);
	// 要搜索的字段
	String filedName = "name";
	// 查询生成器(解析输入生成Query查询对象)
	QueryParser parser = new QueryParser(filedName, analyzer);
	// 通过parse解析输入(分词),生成query对象
	Query query = parser.parse("Thinkpad");
	// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
	TopDocs topDocs = indexSearcher.search(query, 10);   //前10条

	//获得总命中数
	System.out.println(topDocs.totalHits);

	// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
	for (ScoreDoc sdoc : topDocs.scoreDocs) {
		// 根据文档id取存储的文档
		Document hitDoc = indexSearcher.doc(sdoc.doc);
		// 取文档的字段
		System.out.println(hitDoc.get(filedName));
	}

	// 使用完毕,关闭、释放资源
	indexReader.close();
	directory.close();
    }
}

核心API图示:

ac512480ea3a5e68e3d7f773dd682d84730.jpg

搜索核心API详解

IndexReader  索引读取器

Open一个读取器,读取的是该时刻点的索引视图。如果后续索引发生改变,需重新open一个读取器。获得索引读取器的方式:

DirectoryReader.open(IndexWriter indexWriter)       优先使用

DirectoryReader.open(Directory)

DirectoryReader.openIfChanged(DirectoryReader)    共享当前reader资源重新打开一个(当索引变化时)

IndexReader分为两类:

7ad64e1b35b95f9a02874fc891d10da9a62.jpg

叶子读取器:支持获取stored fields, doc values, terms(词项), and postings (词项对应的文档)

复合读取器:多个读取器的复合。只可直接用它获取stored fields 。在内部通过CompositeReader.getSequentialSubReaders 得到里面的叶子读取器来获取其他数据。

DirectoryReader 是 复合读取器

注意:IndexReader是线程安全的。

IndexReader 主要API:

693dea9ac0276832d146ed5ce9f053854ba.jpg

f97299958a97bc2d66d34d0ae9efa2787ab.jpg

IndexSearcher  索引搜索器

应用通过调用它的search(Query,int)重载方法在一个IndexReader上实现搜索。出于性能的考虑,请使用一个IndexSearcher实例,除非索引发生变化。如索引更新了则通过DirectoryReader.openIfChanged(DirectoryReader)  取得新的读取器,再创建新的搜索器。

注意:IndexSearcher是线程安全的。

IndexSearcher  索引搜索器   API

1248611cba7dbb5fe66fea4d449a858bc69.jpg

edeca95f2ab3c5c1fde16901228c4409706.jpg

基本查询详解

Query  查询的表示。 它的可实例化子类有:

3efa999e942e43b3c68269e48803c72a18b.jpg

它们是lucene中的基本查询。我们可以用它们来创建查询。

要掌握的基本查询

42b56289d7005f77b3cd1ffbca5e0287d3c.jpg

1、TermQuery  词项查询

TermQuery tq = new TermQuery(new Term("fieldName", "term"));

词项查询,最基本、最常用的查询。用来查询指定字段包含指定词项的文档

TermQuery tq = new TermQuery(new Term(“name", “thinkpad"));

2、BooleanQuery  布尔查询

搜索的条件往往是多个的,如要查询名称包含“电脑” 或 “thinkpad”的商品,就需要两个词项查询做或合并。布尔查询就是用来组合多个子查询的。每个子查询称为布尔字句 BooleanClause,布尔字句自身也可以是组合的。 组合关系支持如下四种:

Occur.SHOULD          或
Occur.MUST             且
Occur.MUST_NOT          且非
Occur.FILTER    同 MUST,但该字句不参与评分

布尔查询默认的最大字句数为1024,在将通配符查询这样的查询rewriter为布尔查询时,往往会产生很多的字句,可能抛出TooManyClauses 异常。可通过BooleanQuery.setMaxClauseCount(int)设置最大字句数。

BooleanQuery  布尔查询示例

// 布尔查询
Query query1 = new TermQuery(new Term(filedName, "thinkpad"));
Query query2 = new TermQuery(new Term("simpleIntro", "英特尔"));
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
booleanQueryBuilder.add(query1, Occur.SHOULD);
booleanQueryBuilder.add(query2, Occur.MUST);
BooleanQuery booleanQuery = booleanQueryBuilder.build();

// 可像下一行这样写
// BooleanQuery booleanQuery = new BooleanQuery.Builder()
// 	.add(query1, Occur.SHOULD).add(query2, Occur.MUST).build();

3、PhraseQuery  短语查询

最常用的查询,匹配特点序列的多个词项。PhraserQuery使用一个位置移动因子(slop)来决定任意两个词项的位置可最大移动多少个位置来进行匹配,默认为0。有两种方式来构建对象:

915628e7f7267965bc9ac0894075e8e71cc.jpg

PhraseQuery  短语查询示例

PhraseQuery phraseQuery1 = new PhraseQuery("name", "thinkpad",
	"carbon");

PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad",
	"carbon");

PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");

PhraseQuery phraseQuery4 = new PhraseQuery.Builder()
	.add(new Term("name", "笔记本电脑"), 4)
	.add(new Term("name", "联想"), 5).build();
// 这两句等同
PhraseQuery phraseQuery5 = new PhraseQuery.Builder()
	.add(new Term("name", "笔记本电脑"), 0)
	.add(new Term("name", "联想"), 1).build();

PhraseQuery  slop  移动因子说明

String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

1、如果想用  “thinkpad  carbon” 来匹配 name。因中间有 x1,则需要将thinkpad 向右移动1个位置。

2、如果想用  “carbon  thinkpad” 来匹配 name。因中间有 x1,则需要将carbon 向右移动3个位置。

// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

// PhraseQuery 短语查询
PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad","carbon");
// slop示例
PhraseQuery phraseQuery2Slop = new PhraseQuery(3, "name", "carbon",	"thinkpad");
PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");
// slop示例
PhraseQuery phraseQuery3Slop = new PhraseQuery(2, "name", "联想","笔记本电脑");

QueryParserDemo 

public class QueryParserDemo {

	/**
	 * lucene QueryParser示例
	 * 
	 * @throws QueryNodeException
	 */
	public static void main(String[] args)
			throws IOException, ParseException, QueryNodeException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// 要搜索的默认字段
		String defaultFiledName = "name";
		// 查询生成器(解析输入生成Query查询对象)
		QueryParser parser = new QueryParser(defaultFiledName, analyzer);
		// parser.setPhraseSlop(2);
		// 通过parse解析输入,生成query对象
		Query query1 = parser.parse(
				"(name:\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");
		// 等同query1
		Query query2 = parser.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");

		System.out.println("************** query1  ************");
		doSearch(query1, indexSearcher);

		System.out.println("************** query2  ************");
		doSearch(query2, indexSearcher);

		Query query3 = parser.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:[800000 TO 1000000]");

		System.out.println("************** query3  ************");
		doSearch(query3, indexSearcher);

		// 为什么query3查不出结果??? 该如何改
		BooleanQuery bquery = new BooleanQuery.Builder()
				.add(parser
						.parse("(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 "),
						Occur.MUST)
				.add(IntPoint.newRangeQuery("price", 800000, 1000000),
						Occur.MUST)
				.build();

		System.out.println("************** bquery  ************");
		doSearch(bquery, indexSearcher);

		// 传统查询解析器-多默认字段
		String[] multiDefaultFields = { "name", "type", "simpleIntro" };
		MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(
				multiDefaultFields, analyzer);
		// 设置默认的操作
		multiFieldQueryParser.setDefaultOperator(Operator.OR);
		Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");

		System.out.println("************** query4  ************");
		doSearch(query4, indexSearcher);

		StandardQueryParser queryParserHelper = new StandardQueryParser(
				analyzer);
		// 设置默认字段
		// queryParserHelper.setMultiFields(CharSequence[] fields);
		// queryParserHelper.setPhraseSlop(8);
		// Query query = queryParserHelper.parse("a AND b", "defaultField");
		Query query5 = queryParserHelper.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:1999900",
				"name");

		System.out.println("************** query5  ************");
		doSearch(query5, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

 

4、MultiPhraseQuery  多重短语查询

短语查询的一种更通用的用法,支持同位置多个词的OR匹配。通过里面的Builder来构建MultiPhraseQuery:

90f25ef181d23a29e5b621ec20b9fb7a564.jpg

 MultiPhraseQuery 多重短语查询
Term[] terms = new Term[2];
terms[0] = new Term("name", "笔记本");
terms[1] = new Term("name", "笔记本电脑");
Term t = new Term("name", "联想");
MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery.Builder()
	.add(terms).add(t).build();

// 对比 PhraseQuery在同位置加入多个词 ,同位置的多个词都需匹配,所以查不出。
PhraseQuery pquery = new PhraseQuery.Builder().add(terms[0], 0)
	.add(terms[1], 0).add(t, 1).build();

SearchQueryDemo 

public class SearchQueryDemo {

	/**
	 * lucene 搜索查询示例
	 */
	public static void main(String[] args) throws IOException, ParseException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
		// 要搜索的字段
		String filedName = "name";

		// 1、词项查询
		Query query1 = new TermQuery(new Term(filedName, "thinkpad"));
		System.out.println("************** 词项查询 ******************");
		doSearch(query1, indexSearcher);

		// 2、布尔查询
		Query query2 = new TermQuery(new Term("simpleIntro", "英特尔"));
		BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
		booleanQueryBuilder.add(query1, Occur.SHOULD);
		booleanQueryBuilder.add(query2, Occur.MUST);
		BooleanQuery booleanQuery = booleanQueryBuilder.build();

		// 可像下一行这样写
		// BooleanQuery booleanQuery = new BooleanQuery.Builder()
		// .add(query1, Occur.SHOULD).add(query2, Occur.MUST).build();

		System.out.println("************** 布尔查询 ******************");
		doSearch(booleanQuery, indexSearcher);

		// 3、PhraseQuery 短语查询
		// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";
		PhraseQuery phraseQuery1 = new PhraseQuery("name", "thinkpad",
				"carbon");
		System.out.println("************** phrase 短语查询  ******************");
		doSearch(phraseQuery1, indexSearcher);

		PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad",
				"carbon");
		System.out.println("************** phrase 短语查询  ******************");
		doSearch(phraseQuery2, indexSearcher);

		// slop示例
		PhraseQuery phraseQuery2Slop = new PhraseQuery(3, "name", "carbon",
				"thinkpad");
		System.out.println("********** phrase slop 短语查询  ***************");
		doSearch(phraseQuery2Slop, indexSearcher);

		PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");
		System.out.println("************** phrase 短语查询  ******************");
		doSearch(phraseQuery3, indexSearcher);

		// slop示例
		PhraseQuery phraseQuery3Slop = new PhraseQuery(2, "name", "联想",
				"笔记本电脑");
		System.out.println("************** phrase s 短语查询  ******************");
		doSearch(phraseQuery3Slop, indexSearcher);

		PhraseQuery phraseQuery4 = new PhraseQuery.Builder()
				.add(new Term("name", "笔记本电脑"), 4) // 4、5是这个词的位置,和 0、1等同
				.add(new Term("name", "联想"), 5).build();
		System.out.println("********** phrase Builder 1 短语查询  **************");
		doSearch(phraseQuery4, indexSearcher);

		// 等同 phraseQuery4
		PhraseQuery phraseQuery5 = new PhraseQuery.Builder()
				.add(new Term("name", "笔记本电脑"), 0) // 4、5是这个词的位置,和 0、1等同
				.add(new Term("name", "联想"), 1).build();
		System.out.println("*********** phrase Builder 2  短语查询  ***********");
		doSearch(phraseQuery5, indexSearcher);

		// 4 MultiPhraseQuery 多重短语查询
		Term[] terms = new Term[2];
		terms[0] = new Term("name", "笔记本");
		terms[1] = new Term("name", "笔记本电脑");
		Term t = new Term("name", "联想");
		MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery.Builder()
				.add(terms).add(t).build();
		System.out.println(
				"************** multiPhraseQuery 短语查询  ******************");
		doSearch(multiPhraseQuery, indexSearcher);

		// 对比 PhraseQuery在同位置加入多个词 ,同位置的多个词都需匹配,所以查不出。
		PhraseQuery pquery = new PhraseQuery.Builder().add(terms[0], 0)
				.add(terms[1], 0).add(t, 1).build();
		System.out.println(
				"************** multiPhraseQuery  对比 PhraseQuery 短语查询  ******************");
		doSearch(pquery, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

5、SpanNearQuery  临近查询(跨度查询)

用于更复杂的短语查询,可以指定词间位置的最大间隔跨度。通过组合一系列的SpanQuery 实例来进行查询,可以指定是否按顺序匹配、slop、gap。

1adcf43cba5d1bbfca1148a6f8aac9080b1.jpg

SpanNearQuery 临近查询示例

// SpanNearQuery 临近查询
SpanTermQuery tq1 = new SpanTermQuery(new Term("name", "thinkpad"));
SpanTermQuery tq2 = new SpanTermQuery(new Term("name", "carbon"));
SpanNearQuery spanNearQuery = new SpanNearQuery(
	new SpanQuery[] { tq1, tq2 }, 1, true);

// SpanNearQuery 临近查询 gap slop 使用
SpanNearQuery.Builder spanNearQueryBuilder = SpanNearQuery
	.newOrderedNearQuery("name");
spanNearQueryBuilder.addClause(tq1).addGap(0).setSlop(1)
	.addClause(tq2);
SpanNearQuery spanNearQuery5 = spanNearQueryBuilder.build();

SpanNearQueryDemo 

public class SpanNearQueryDemo {

	/**
	 * lucene 搜索查询示例
	 */
	public static void main(String[] args) throws IOException, ParseException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

		// SpanNearQuery 临近查询
		SpanTermQuery tq1 = new SpanTermQuery(new Term("name", "thinkpad"));
		SpanTermQuery tq2 = new SpanTermQuery(new Term("name", "carbon"));
		SpanNearQuery spanNearQuery = new SpanNearQuery(
				new SpanQuery[] { tq1, tq2 }, 1, true);

		System.out.println("************** SpanNearQuery 临近查询  ************");
		doSearch(spanNearQuery, indexSearcher);

		// 下面的例子词是反序的
		SpanNearQuery spanNearQuery2 = new SpanNearQuery(
				new SpanQuery[] { tq2, tq1 }, 1, true);

		System.out.println(
				"************** SpanNearQuery 临近查询 2 1,true************");
		doSearch(spanNearQuery2, indexSearcher);

		SpanNearQuery spanNearQuery3 = new SpanNearQuery(
				new SpanQuery[] { tq2, tq1 }, 3, true);

		System.out.println(
				"************** SpanNearQuery 临近查询 3  3, true************");
		doSearch(spanNearQuery3, indexSearcher);

		SpanNearQuery spanNearQuery4 = new SpanNearQuery(
				new SpanQuery[] { tq2, tq1 }, 3, false);

		System.out.println(
				"************** SpanNearQuery 临近查询 4  3, false************");
		doSearch(spanNearQuery4, indexSearcher);

		// SpanNearQuery 临近查询 gap slop 使用 1
		SpanTermQuery ctq1 = new SpanTermQuery(new Term("name", "张三"));
		SpanTermQuery ctq2 = new SpanTermQuery(new Term("name", "在理"));
		SpanNearQuery.Builder spanNearQueryBuilder = SpanNearQuery
				.newOrderedNearQuery("name");
		spanNearQueryBuilder.addClause(ctq1).addGap(0).setSlop(2)
				.addClause(ctq2);

		System.out.println("************** SpanNearQuery 临近查询  ************");
		doSearch(spanNearQueryBuilder.build(), indexSearcher);

		// SpanNearQuery 临近查询 gap slop 使用 2
		SpanNearQuery.Builder spanNearQueryBuilder2 = SpanNearQuery
				.newOrderedNearQuery("name");
		spanNearQueryBuilder2.addClause(ctq1).addGap(2).setSlop(0)
				.addClause(ctq2);

		System.out.println("************** SpanNearQuery 临近查询  ************");
		doSearch(spanNearQueryBuilder2.build(), indexSearcher);

		// SpanNearQuery 临近查询 gap slop 使用 3
		SpanNearQuery.Builder spanNearQueryBuilder3 = SpanNearQuery
				.newOrderedNearQuery("name");
		spanNearQueryBuilder3.addClause(ctq1).addGap(1).setSlop(1)
				.addClause(ctq2);

		System.out.println("************** SpanNearQuery 临近查询  ************");
		doSearch(spanNearQueryBuilder3.build(), indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

6、TermRangeQuery  词项范围查询

用于查询包含某个范围内的词项的文档,如以字母开头a到c的词项。词项在反向索引中是排序的,只需指定的开始词项、结束词项,就可以查询该范围的词项。 如果是做数值的范围查询则用 PointRangeQuery 。

f9dde0b5d99edb4fddc94c75fc5d494ee5b.jpg

TermRangeQuery  词项范围查询示例

// TermRangeQuery 词项范围查询
TermRangeQuery termRangeQuery = TermRangeQuery.newStringRange("name",
	"carbon", "张三", false, true);

TermRangeQueryDemo 

public class TermRangeQueryDemo {

	/**
	 * lucene 搜索查询示例
	 */
	public static void main(String[] args) throws IOException, ParseException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

		// TermRangeQuery 词项范围查询
		TermRangeQuery termRangeQuery = TermRangeQuery.newStringRange("name",
				"carbon", "张三", false, true);

		System.out.println("********** TermRangeQuery 词项范围查询  ***********");
		doSearch(termRangeQuery, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

7、PrefixQuery, WildcardQuery, RegexpQuery

PrefixQuery  前缀查询     查询包含以xxx为前缀的词项的文档,是通配符查询,如 app,实际是 app* 
WildcardQuery   通配符查询     *表示0个或多个字符,?表示1个字符,\是转义符。通配符查询可能会比较慢,不可以通配符开头(那样就是所有词项了) 
RegexpQuery  正则表达式查询     词项符合某正则表达式

注意:这三种查询可能会比较慢,使用时+谨慎

PrefixQuery, WildcardQuery, RegexpQuery 示例

// PrefixQuery 前缀查询
PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));

// WildcardQuery 通配符查询
WildcardQuery wildcardQuery = new WildcardQuery(
	new Term("name", "think*"));

// WildcardQuery 通配符查询
WildcardQuery wildcardQuery2 = new WildcardQuery(
	new Term("name", "厉害了???"));

// RegexpQuery 正则表达式查询
RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厉害.{4}"));

8、FuzzyQuery 模糊查询

简单地与索引词项进行相近匹配,允许最大2个不同字符。常用于拼写错误的容错:如把 “thinkpad” 拼成 “thinkppd”或 “thinkd”,使用FuzzyQuery 仍可搜索到正确的结果。

// FuzzyQuery 模糊查询
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("name", "thind"));

FuzzyQuery fuzzyQuery2 = new FuzzyQuery(new Term("name", "thinkd"), 2);

FuzzyQuery fuzzyQuery3 = new FuzzyQuery(new Term("name", "thinkpaddd"));

FuzzyQuery fuzzyQuery4 = new FuzzyQuery(new Term("name", "thinkdaddd"));

PrefixWildcardRegexpFuzzyQueryDemo

public class PrefixWildcardRegexpFuzzyQueryDemo {

	/**
	 * lucene 搜索查询示例
	 */
	public static void main(String[] args) throws IOException, ParseException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

		// PrefixQuery 前缀查询
		PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));
		System.out.println("********** PrefixQuery 前缀查询  ***********");
		doSearch(prefixQuery, indexSearcher);

		// WildcardQuery 通配符查询
		WildcardQuery wildcardQuery = new WildcardQuery(
				new Term("name", "think*"));

		System.out.println("********** WildcardQuery 通配符  ***********");
		doSearch(wildcardQuery, indexSearcher);

		// WildcardQuery 通配符查询
		WildcardQuery wildcardQuery2 = new WildcardQuery(
				new Term("name", "厉害了???"));
		System.out.println("********** WildcardQuery 通配符  ***********");
		doSearch(wildcardQuery2, indexSearcher);

		// RegexpQuery 正则表达式查询
		RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厉害.{4}"));
		System.out.println("**********RegexpQuery 正则表达式查询***********");
		doSearch(regexpQuery, indexSearcher);

		// FuzzyQuery 模糊查询
		FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("name", "thind"));
		System.out.println("**********FuzzyQuery 模糊查询***********");
		doSearch(fuzzyQuery, indexSearcher);

		// FuzzyQuery 模糊查询
		FuzzyQuery fuzzyQuery2 = new FuzzyQuery(new Term("name", "thinkd"), 2);
		System.out.println("**********FuzzyQuery 模糊查询***********");
		doSearch(fuzzyQuery2, indexSearcher);

		// FuzzyQuery 模糊查询
		FuzzyQuery fuzzyQuery3 = new FuzzyQuery(new Term("name", "thinkpaddd"));
		System.out.println("**********FuzzyQuery 模糊查询***********");
		doSearch(fuzzyQuery3, indexSearcher);

		// FuzzyQuery 模糊查询
		FuzzyQuery fuzzyQuery4 = new FuzzyQuery(new Term("name", "thinkdaddd"));
		System.out.println("**********FuzzyQuery 模糊查询***********");
		doSearch(fuzzyQuery4, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

9、数值查询

前提:查询的数值字段必须索引。通过 IntPoint, LongPoint, FloatPoint, or DoublePoint 中的方法构建对应的查询。以IntPoint为例:

5ac51c2c9a764049bcbb9f58ac452fc5173.jpg

数值查询示例

// 精确值查询
Query exactQuery = IntPoint.newExactQuery("price", 1999900);
// 数值范围查询
Query pointRangeQuery = IntPoint.newRangeQuery("price", 499900,1000000);
// 集合查询
Query setQuery = IntPoint.newSetQuery("price", 1999900, 1000000,2000000);

PointQueryDemo

public class PointQueryDemo {

	/**
	 * lucene 搜索查询示例
	 */
	public static void main(String[] args) throws IOException, ParseException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// 精确值查询
		Query exactQuery = IntPoint.newExactQuery("price", 1999900);
		System.out.println("********** pointRangeQuery 数值精确查询  ***********");
		doSearch(exactQuery, indexSearcher);

		// PointRangeQuery 数值范围查询
		Query pointRangeQuery = IntPoint.newRangeQuery("price", 499900,
				1000000);
		System.out.println("********** pointRangeQuery 数值范围查询  ***********");
		doSearch(pointRangeQuery, indexSearcher);

		// 集合查询
		Query setQuery = IntPoint.newSetQuery("price", 1999900, 1000000,
				2000000);
		System.out.println("********** pointRangeQuery 数值集合查询  ***********");
		doSearch(setQuery, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

查询小结

4258e6de994b48af527c36b627643472ed6.jpg

问1:如果用户输入了“联想笔记本电脑”来搜索商品名称或商品简介中包含这个短语的商品,这个查询我们该如何构建?

查询小结-练习

问题1:如果用户输入了“联想笔记本电脑”来搜索商品名称或商品简介中包含这个短语的商品,这个查询我们该如何构建?

1、分别在商品名、简介字段上构建短语查询

2、将两个短语查询作OR组合成布尔查询

问题2:用户的查询需求变了:如要查询:名称或简介中包含“联想笔记本电脑”, 且类别是“电脑”,且价格8000-100000元的商品,该如何构建查询?

1、分别在商品名、简介字段上构建短语查询

2、将两个短语查询作OR组合成布尔查询

3、在类别字段上建立词项查询,再与前面的布尔查询做AND组合

4、在价格字段上建立数值范围查询,再与前面的做AND组合

查询小结-拓展

问题3:如果用户查询需求又变了,你怎么办?     又变了、又变了、又变了….    你怎么办?

结论:用户的查询需求是多变的,我们无法事先知道,也就无法事先编写好构建查询的代码。

思考1:不能事先知道用户的查询需求,能不能要用户在输入时,不光输入查询值,把他的查询需求也一并输入?

思考2:如果要这样做,是不是得在我们和用户之间建立一套查询需求的描述规则?

思考3:这套规则应该是怎样的?回顾问题1、问题2的答案,请看他们有什么想通的地方没?

结论:不同的查询需求只是不同字段的不同基本查询的组合。

查询需求描述规则可不可以像下面这样:

(name:"联想笔记本电脑" OR simpleIntro :"联想笔记本电脑") AND type:电脑 AND price:[800000 TO 1000000]

用户的查询需求被很好的描述出来了,我们的搜索程序中得能解读这个描述,并把它转为对应的查询组合。这就是 QueryParser包的功能。

QueryParser详解

核心API图示:

9653a1f5a47456082e0d44bfe25e26abe54.jpg

QueryParser 查询解析生成器

Lucene QueryPaser包中提供了两类查询解析器:

1.传统的解析器         

QueryParser         MultiFieldQueryParser

2.基于新的 flexible 框架的解析器

StandardQueryParser

两种解析框架,一套查询描述规则。

用法1  传统解析器-单默认字段   QueryParser:

QueryParser parser = new QueryParser("defaultFiled", analyzer);
//parser.setPhraseSlop(2);
Query query = parser.parse("query String");

示例:

// 使用的分词器
Analyzer analyzer = new IKAnalyzer4Lucene7(true);
// 要搜索的默认字段
String defaultFiledName = "name";
// 查询生成器(解析输入生成Query查询对象)
QueryParser parser = new QueryParser(defaultFiledName, analyzer);
// 通过parse解析输入,生成query对象
Query query1 = parser.parse(
		"(name:\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");

用法2  传统解析器-多默认字段  MultiFieldQueryParser:

// 传统查询解析器-多默认字段
String[] multiDefaultFields = { "name", "type", "simpleIntro" };
MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(
		multiDefaultFields, analyzer);
// 设置默认的组合操作,默认是 OR
multiFieldQueryParser.setDefaultOperator(Operator.OR);
Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");

用法3  新解析框架的标准解析器:StandardQueryParser:

StandardQueryParser queryParserHelper = new StandardQueryParser(analyzer);
// 设置默认字段
// queryParserHelper.setMultiFields(CharSequence[] fields);
// queryParserHelper.setPhraseSlop(8);
// Query query = queryParserHelper.parse("a AND b", "defaultField");
Query query5 = queryParserHelper.parse(
	"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:1999900","name");

QueryParserDemo

public class QueryParserDemo {

	/**
	 * lucene QueryParser示例
	 * 
	 * @throws QueryNodeException
	 */
	public static void main(String[] args)
			throws IOException, ParseException, QueryNodeException {
		// 使用的分词器
		Analyzer analyzer = new IKAnalyzer4Lucene7(true);
		// 索引存储目录
		Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));
		// 索引读取器
		IndexReader indexReader = DirectoryReader.open(directory);
		// 索引搜索器
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);

		// 要搜索的默认字段
		String defaultFiledName = "name";
		// 查询生成器(解析输入生成Query查询对象)
		QueryParser parser = new QueryParser(defaultFiledName, analyzer);
		// parser.setPhraseSlop(2);
		// 通过parse解析输入,生成query对象
		Query query1 = parser.parse(
				"(name:\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");
		// 等同query1
		Query query2 = parser.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");

		System.out.println("************** query1  ************");
		doSearch(query1, indexSearcher);

		System.out.println("************** query2  ************");
		doSearch(query2, indexSearcher);

		Query query3 = parser.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:[800000 TO 1000000]");

		System.out.println("************** query3  ************");
		doSearch(query3, indexSearcher);

		// 为什么query3查不出结果??? 该如何改
		BooleanQuery bquery = new BooleanQuery.Builder()
				.add(parser
						.parse("(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 "),
						Occur.MUST)
				.add(IntPoint.newRangeQuery("price", 800000, 1000000),
						Occur.MUST)
				.build();

		System.out.println("************** bquery  ************");
		doSearch(bquery, indexSearcher);

		// 传统查询解析器-多默认字段
		String[] multiDefaultFields = { "name", "type", "simpleIntro" };
		MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(
				multiDefaultFields, analyzer);
		// 设置默认的操作
		multiFieldQueryParser.setDefaultOperator(Operator.OR);
		Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");

		System.out.println("************** query4  ************");
		doSearch(query4, indexSearcher);

		StandardQueryParser queryParserHelper = new StandardQueryParser(
				analyzer);
		// 设置默认字段
		// queryParserHelper.setMultiFields(CharSequence[] fields);
		// queryParserHelper.setPhraseSlop(8);
		// Query query = queryParserHelper.parse("a AND b", "defaultField");
		Query query5 = queryParserHelper.parse(
				"(\"联想笔记本电脑\" OR simpleIntro:英特尔) AND type:电脑 AND price:1999900",
				"name");

		System.out.println("************** query5  ************");
		doSearch(query5, indexSearcher);

		// 使用完毕,关闭、释放资源
		indexReader.close();
		directory.close();
	}

	private static void doSearch(Query query, IndexSearcher indexSearcher)
			throws IOException {
		// 打印输出查询
		System.out.println("query:  " + query.toString());

		// 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))
		TopDocs topDocs = indexSearcher.search(query, 10); // 前10条

		System.out.println("**** 查询结果 ");
		// 获得总命中数
		System.out.println("总命中数:" + topDocs.totalHits);
		// 遍历topN结果的scoreDocs,取出文档id对应的文档信息
		for (ScoreDoc sdoc : topDocs.scoreDocs) {
			// 根据文档id取存储的文档
			Document hitDoc = indexSearcher.doc(sdoc.doc);
			System.out.println("-------------- docId=" + sdoc.doc + ",score="
					+ sdoc.score);
			// 取文档的字段
			System.out.println("prodId:" + hitDoc.get("prodId"));
			System.out.println("name:" + hitDoc.get("name"));
			System.out.println("simpleIntro:" + hitDoc.get("simpleIntro"));
			System.out.println("price:" + hitDoc.get("price"));

			System.out.println();
		}

	}
}

 

使用查询解析器前需考虑三点:

1.查询字符串应是由人输入的,而不应是你编程产生。如果你为了用查询解析器,而在你的应用中编程产生查询字符串,不可取,更应该直接使用基本查询API;

2.未分词的字段,应直接使用基本查询API加入到查询中,而不应使用查询解析器;

3.对于普通文本字段,使用查询解析器,而其他值字段:如 时间、数值,则应使用基本查询API

 

查询描述规则语法(查询解析语法):

Term 词项:

单个词项的表示:     电脑

短语的表示:     "联想笔记本电脑"

Field 字段:

字段名:

示例: name:“联想笔记本电脑” AND type:电脑

如果name是默认字段,则可写成: “联想笔记本电脑” AND type:电脑

如果查询串是:type:电脑 计算机 手机

注意:只有第一个是type的值,后两个则是使用默认字段。

Term Modifiers 词项修饰符:

统配符:

?    单个字符
*    0个或多个字符
示例:te?t    test*    te*t
注意:通配符不可用在开头。

模糊查询:词后加 ~

示例:     roam~
模糊查询最大支持两个不同字符。
示例:  roam~1

正则表达式:   /xxxx/

示例:     /[mb]oat/

临近查询:短语后加 ~移动值

示例: "jakarta apache"~10

范围查询:

mod_date:[20020101 TO 20030101]       包含边界值
title:{Aida TO Carmen}      不包含边界值

词项加权:使该词项的相关性更高,通过 ^数值来指定加权因子,默认加权因子值是1

示例:如要搜索包含 jakarta apache 的文章,jakarta更相关,则:jakarta^4 apache
短语也可以: "jakarta apache"^4 "Apache Lucene

Boolean 操作符:

Lucene支持的布尔操作: AND, “+”, OR, NOT ,"-"

OR:"jakarta apache" jakarta = "jakarta apache" OR jakarta

AND:"jakarta apache" AND "Apache Lucene"

+  必须包含:  +jakarta lucene

NOT   非:"jakarta apache" NOT "Apache Lucene“   注意:NOT不可单项使用: NOT “Apache Lucene“     不可以

-    同NOT:"jakarta apache"  -"Apache Lucene“

组合 () 

字句组合    (jakarta OR apache) AND website

字段组合    title:(+return +"pink panther")

转义   \

对语法字符: + - && || ! ( ) { } [ ] ^ “ ~ * ? : \ /     进行转义。

如要查询包含 (1+1):2   
     \(1\+1\)\:2 

 

 

转载于:https://my.oschina.net/u/3728166/blog/3006855

写在前面的话 引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体CD-ROM 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础 1.5.2 等价和似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 1.6.2 抽象的基础和接口 1.7 对象的创建和存在时间 1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:解决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2章 一切都是对象 2.1 用句柄操纵对象 2.2 必须创建所有对象 2.2.1 保存在什么地方 2.2.2 特殊情况:主型 2.2.3 Java中的数组 2.3 绝对不要清除对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 新建数据型: 2.4.1 字段和方法 2.5 方法、自变量和返回值 2.5.1 自变量列表 2.6 构建Java程序 2.6.1 名字的可见性 2.6.2 使用其他组件 2.6.3 static关键字 2.7 我们的第一个Java程序 2.8 注释和嵌入文档 2.8.1 注释文档 2.8.2 具体语法 2.8.3 嵌入HTML 2.8.4 @see:引用其他 2.8.5 文档标记 2.8.6 变量文档标记 2.8.7 方法文档标记 2.8.8 文档示例 2.9 编码样式 2.10 总结 2.11 练习 第3章 控制程序流程 3.1 使用Java运算符 3.1.1 优先级 3.1.2 赋值 3.1.3 算术运算符 3.1.4 自动递增和递减 3.1.5 关系运算符 3.1.6 逻辑运算符 3.1.7 按位运算符 3.1.8 移位运算符 3.1.9 三元if-else运算符 3.1.10 逗号运算符 3.1.11 字串运算符+ 3.1.12 运算符常规操作规则 3.1.13 造型运算符 3.1.14 Java没有“sizeof” 3.1.15 复习计算顺序 3.1.16 运算符总结 3.2 执行控制 3.2.1 真和假 3.2.2 if-else 3.2.3 反复 3.2.4 do-while 3.2.5 for 3.2.6 中断和继续 3.2.7 切换 3.3 总结 3.4 练习 第4章 初始化和清除 4.1 由构建器保证初始化 4.2 方法过载 4.2.1 区分过载方法 4.2.2 主型的过载 4.2.3 返回值过载 4.2.4 默认构建器 4.2.5 this关键字 4.3 清除:收尾和垃圾收集 4.3.1 finalize()用途何在 4.3.2 必须执行清除 4.4 成员初始化 4.4.1 规定初始化 4.4.2 构建器初始化 4.5 数组初始化 4.5.1 多维数组 4.6 总结 4.7 练习 第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 访问 5.5 总结 5.6 练习 第6章 再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础 6.3 合成与继承的结合 6.3.1 确保正确的清除 6.3.2 名字的隐藏 6.4 到底选择合成还是继承 6.5 protected 6.6 递增开发 6.7 上溯造型 6.7.1 何谓“上溯造型”? 6.8 final关键字 6.8.1 final数据 6.8.2 final方法 6.8.3 final 6.8.4 final的注意事项 6.9 初始化和装载 6.9.1 继承初始化 6.10 总结 6.11 练习 第7章 多形性 7.1 上溯造型 7.1.1 为什么要上溯造型 7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象和方法 7.5 接口 7.5.1 Java的“多重继承” 7.5.2 通过继承扩展接口 7.5.3 常数分组 7.5.4 初始化接口中的字段 7.6 内部 7.6.1 内部和上溯造型 7.6.2 方法和作用域中的内部 7.6.3 链接到外部 7.6.4 static内部 7.6.5 引用外部对象 7.6.6 从内部继承 7.6.7 内部可以覆盖吗? 7.6.8 内部标识符 7.6.9 为什么要用内部:控制框架 7.7 构建器和多形性 7.7.1 构建器的调用顺序 7.7.2 继承和finalize() 7.7.3 构建器内部的多形性方法的行为 7.8 通过继承进行设计 7.8.1 纯继承与扩展 7.8.2 下溯造型与运行期型标识 7.9 总结 7.10 练习 第8章 对象的容纳 8.1 数组 8.1.1 数组和第一对象 8.1.2 数组的返回 8.2 集合 8.2.1 缺点:型未知 8.3 枚举器(反复器) 8.4 集合的型 8.4.1 Vector 8.4.2 BitSet 8.4.3 Stack 8.4.4 Hashtable 8.4.5 再论枚举器 8.5 排序 8.6 通用集合库 8.7 新集合 8.7.1 使用Collections 8.7.2 使用Lists 8.7.3 使用Sets 8.7.4 使用Maps 8.7.5 决定实施方案 8.7.6 未支持的操作 8.7.7 排序和搜索 8.7.8 实用工具 8.8 总结 8.9 练习 第9章 违例差错控制 9.1 基本违例 9.1.1 违例自变量 9.2 违例的捕获 9.2.1 try块 9.2.2 违例控制器 9.2.3 违例规范 9.2.4 捕获所有违例 9.2.5 重新“掷”出违例 9.3 标准Java违例 9.3.1 RuntimeException的特殊情况 9.4 创建自己的违例 9.5 违例的限制 9.6 用finally清除 9.6.1 用finally做什么 9.6.2 缺点:丢失的违例 9.7 构建器 9.8 违例匹配 9.8.1 违例准则 9.9 总结 9.10 练习 第10章 Java IO系统 10.1 输入和输出 10.1.1 InputStream的型 10.1.2 OutputStream的型 10.2 增添属性和有用的接口 10.2.1 通过FilterInputStream从InputStream里读入数据 10.2.2 通过FilterOutputStream向OutputStream里写入数据 10.3 本身的缺陷:RandomAccessFile 10.4 File 10.4.1 目录列表器 10.4.2 检查与创建目录 10.5 IO流的典型应用 10.5.1 输入流 10.5.2 输出流 10.5.3 快捷文件处理 10.5.4 从标准输入中读取数据 10.5.5 管道数据流 10.6 StreamTokenizer 10.6.1 StringTokenizer 10.7 Java 1.1的IO流 10.7.1 数据的发起与接收 10.7.2 修改数据流的行为 10.7.3 未改变的 10.7.4 一个例子 10.7.5 重定向标准IO 10.8 压缩 10.8.1 用GZIP进行简单压缩 10.8.2 用Zip进行多文件保存 10.8.3 Java归档(jar)实用程序 10.9 对象串联 10.9.1 寻找 10.9.2 序列化的控制 10.9.3 利用“持久性” 10.10 总结 10.11 练习 第11章 运行期型鉴定 11.1 对RTTI的需要 11.1.1 Class对象 11.1.2 造型前的检查 11.2 RTTI语法 11.3 反射:运行期信息 11.3.1 一个方法提取器 11.4 总结 11.5 练习 第12章 传递和返回对象 12.1 传递句柄 12.1.1 别名问题 12.2 制作本地副本 12.2.1 按值传递 12.2.2 克隆对象 12.2.3 使具有克隆能力 12.2.4 成功的克隆 12.2.5 Object.clone()的效果 12.2.6 克隆合成对象 12.2.7 用Vector进行深层复制 12.2.8 通过序列化进行深层复制 12.2.9 使克隆具有更大的深度 12.2.10 为什么有这个奇怪的设计 12.3 克隆的控制 12.3.1 副本构建器 12.4 只读 12.4.1 创建只读 12.4.2 “一成不变”的弊端 12.4.3 不变字串 12.4.4 String和StringBuffer 12.4.5 字串的特殊性 12.5 总结 12.6 练习 第13章 创建窗口和程序片 13.1 为何要用AWT? 13.2 基本程序片 13.2.1 程序片的测试 13.2.2 一个更图形化的例子 13.2.3 框架方法的演示 13.3 制作按钮 13.4 捕获事件 13.5 文本字段 13.6 文本区域 13.7 标签 13.8 复选框 13.9 单选钮 13.10 下拉列表 13.11 列表框 13.11.1 handleEvent() 13.12 布局的控制 13.12.1 FlowLayout 13.12.2 BorderLayout 13.12.3 GridLayout 13.12.4 CardLayout 13.12.5 GridBagLayout 13.13 action的替用品 13.14 程序片的局限 13.14.1 程序片的优点 13.15 视窗化应用 13.15.1 菜单 13.15.2 对话框 13.16 新型AWT 13.16.1 新的事件模型 13.16.2 事件和接收者型 13.16.3 用Java 1.1 AWT制作窗口和程序片 13.16.4 再探早期示例 13.16.5 动态绑定事件 13.16.6 将商业逻辑与UI逻辑区分开 13.16.7 推荐编码方法 13.17 Java 1.1 UI API 13.17.1 桌面颜色 13.17.2 打印 13.17.3 剪贴板 13.18 可视编程和Beans 13.18.1 什么是Bean 13.18.2 用Introspector提取BeanInfo 13.18.3 一个更复杂的Bean 13.18.4 Bean的封装 13.18.5 更复杂的Bean支持 13.18.6 Bean更多的知识 13.19 Swing入门 13.19.1 Swing有哪些优点 13.19.2 方便的转换 13.19.3 显示框架 13.19.4 工具提示 13.19.5 边框 13.19.6 按钮 13.19.7 按钮组 13.19.8 图标 13.19.9 菜单 13.19.10 弹出式菜单 13.19.11 列表框和组合框 13.19.12 滑杆和进度指示条 13.19.13 树 13.19.14 表格 13.19.15 卡片式对话框 13.19.16 Swing消息框 13.19.17 Swing更多的知识 13.20 总结 13.21 练习 第14章 多线程 14.1 反应灵敏的用户界面 14.1.1 从线程继承 14.1.2 针对用户界面的多线程 14.1.3 用主合并线程 14.1.4 制作多个线程 14.1.5 Daemon线程 14.2 共享有限的资源 14.2.1 资源访问的错误方法 14.2.2 Java如何共享资源 14.2.3 回顾Java Beans 14.3 堵塞 14.3.1 为何会堵塞 14.3.2 死锁 14.4 优先级 14.4.1 线程组 14.5 回顾runnable 14.5.1 过多的线程 14.6 总结 14.7 练习 第15章 网络编程 15.1 机器的标识 15.1.1 服务器和客户机 15.1.2 端口:机器内独一无二的场所 15.2 套接字 15.2.1 一个简单的服务器和客户机程序 15.3 服务多个客户 15.4 数据报 15.5 一个Web应用 15.5.1 服务器应用 15.5.2 NameSender程序片 15.5.3 15.5.3 要注意的问题 15.6 Java与CGI的沟通 15.6.1 CGI数据的编码 15.6.2 程序片 15.6.3 用C++写的CGI程序 15.6.4 POST的概念 15.7 用JDBC连接数据库 15.7.1 获得学习示例 15.7.2 查找程序的GUI版本 15.7.3 JDBC API为何如何复杂 15.8 远程方法 15.8.1 远程接口概念 15.8.2 远程接口的实施 15.8.3 创建根与干 15.8.4 使用远程对象 15.8.5 RMI的替选方案 15.9 总结 15.10 练习 第16章 设计范式 16.1 范式的概念 16.1.1 单子 16.1.2 范式分 16.2 观察器范式 16.3 模拟垃圾回收站 16.4 改进设计 16.4.1 “制作更多的对象” 16.4.2 用于原型创建的一个范式 16.5 抽象的应用 16.6 多重派遣 16.6.1 实现双重派遣 16.7 访问器范式 16.8 RTTI有害吗 16.9 总结 16.10 练习 第17章 项目 17.1 文字处理 17.1.1 提取代码列表 17.1.2 检查大小写样式 17.2 方法查找工具 17.3 复杂性理论 17.4 总结 17.5 练习 附录A 使用非Java代码 A.1 Java固有接口 A.1.1 调用固有方法 A.1.2 访问JNI函数:JNIEnv自变量 A.1.3 传递和使用Java对象 A.1.4 JNI和Java违例 A.1.5 JNI和线程处理 A.1.6 使用现成代码 A.2 微软的解决方案 A.3 J/Direct A.3.1 @dll.import引导命令 A.3.2 com.ms.win32包 A.3.3 汇集 A.3.4 编写回调函数 A.3.5 其他J/Direct特性 A.4 本原接口(RNI) A.4.1 RNI总结 A.5 Java/COM集成 A.5.1 COM基础 A.5.2 MS Java/COM集成 A.5.3 用Java设计COM服务器 A.5.4 用Java设计COM客户 A.5.5 ActiveX/Beans集成 A.5.6 固有方法与程序片的注意事项 A.6 CORBA A.6.1 CORBA基础 A.6.2 一个例子 A.6.3 Java程序片和CORBA A.6.4 比较CORBA与RMI A.7 总结 附录B 对比C++和Java 附录C Java编程规则 附录D 性能 D.1 基本方法 D.2 寻找瓶颈 D.2.1 安插自己的测试代码 D.2.2 JDK性能评测[2] D.2.3 特殊工具 D.2.4 性能评测的技巧 D.3 提速方法 D.3.1 常规手段 D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值