布尔运算--java位图搜索实现

前言

布尔运算是伟大的布尔发明的代数运算,只有简单的逻辑与或非,一开始人们没发现没啥用,后来对计算机的影响太大了,从基础设施到搜索引擎无处不在。

场景

身为码农,在日常工作中,我也遇到了涉及它的需求。场景是这样的,我们的后台服务有一个复杂的配置,涉及到对用户多个维度的匹配,因为变化不会很频繁,

每次都查询数据库显然不划算,数据量也不是太多,不到万的级别,人配置的嘛。    这样很自然的,缓存到服务器的内存吧,但是总不能蛮力的一个个匹配吧,也太啰嗦,效率也会很低,而且逻辑判断会有点复杂,配置主要是四个维度:机型、渠道、国家、版本,每个维度都是一个数组,有的值是一个["ALL"],意思是全部匹配;

引言

于是想到了布尔运算,比较简单,每个值都对应一长串数字位,有多少条数据,每个维度的每个值就是最多多少位。

如总共10000条配置,则“中国”对应的位向量最多10000位,对于“中国”这个值,第100位是1就表示第100条配置包含这个"中国"维度值。

查询国家是“中国”的就是从map中取“中国”对应的位向量和"ALL"对应的位向量做 或 运算。Java自带了大整数的实现:BigInteger;可以给构造方法传递一个代表二进制位的byte数组,byte数组的长度显然是: (配置条数/8)+1,BigInteger内部还会做些处理,主要是去除左边连续的0;

马上开始

由于我们是用的MongoDB数据库,操作是用的spring-data对mongoDB的封装。有Criteria和Query的API,这也是spring一贯的风格。

于是,实现了一个通用的位图查询,和Criteria的API相似,这样代码改动可以很小(把import spring的包换成自己的就差不多完事了),不啰嗦了,上代码:

先上测试case(例子有点low,将就看吧):

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Assert;
import org.junit.Test;

import bitmapSearch.BitmapSearcher;
import bitmapSearch.Criteria;

public class BitmapSearchTest {

	@Test
	public void testQuery() {
		List<Car> cars = Arrays.asList(new Car(), //
				new Car("大众").setToAreas("ALL"), //
				new Car("耗子车", 3).setToAreas("某山区"), //
				new Car("东方红", 6).setToAreas("中国"));
		BitmapSearcher searcher = new BitmapSearcher(cars, new BitmapSearcher.IndexCreator<Car, String>() {
			@Override
			public String[] indexKeys() {
				return new String[] { "id", "brand", "legs", "toAreas", "desc" };
			}

			@Override
			public Object[] fieldValue(Car bean, String indexKey) {
				if ("id".equals(indexKey)) {
					return new Object[] { bean.id };
				} else if ("brand".equals(indexKey)) {
					return new Object[] { bean.getBrand() };
				} else if ("legs".equals(indexKey)) {
					return new Object[] { bean.getLegs() };
				} else if ("toAreas".equals(indexKey)) {
					return bean.getToAreas();
				}else if ("desc".equals(indexKey)) {
					return new Object[] { bean.getDesc() };
				}
				return null;
			}
		});
		Car rs1 = searcher.findOne(
				Criteria.where("legs").is(6)//
						.andOperator(new Criteria().orOperator(//
								Criteria.where("id").is(4), //
								Criteria.where("desc").is("MadeInChina")))//
				, null);//
		Assert.assertTrue(rs1.brand.equals("东方红"));
		List<Car> rs2 = searcher.find(Criteria.where("toAreas").in("中国", "ALL"));
		Assert.assertTrue(rs2 != null && rs2.size() == 2);
	}

	private static class Car {
		final int id;
		static AtomicInteger ID_GEN = new AtomicInteger();
		String brand = "QQ";
		int legs = 4;
		String[] toAreas;
		String desc = "";

		public Car() {
			super();
			id = ID_GEN.incrementAndGet();
		}

		public Car(String brand) {
			this();
			this.brand = brand;
		}

		public Car(String brand, int legs) {
			this();
			this.brand = brand;
			this.legs = legs;
		}

		public String getBrand() {
			return brand;
		}

		public void setBrand(String brand) {
			this.brand = brand;
		}

		public int getLegs() {
			return legs;
		}

		public void setLegs(int legs) {
			this.legs = legs;
		}

		public Car setToAreas(String... toAreas) {
			this.toAreas = toAreas;
			return this;
		}

		public String[] getToAreas() {
			return toAreas;
		}

		public String getDesc() {
			return desc;
		}

		public void setDesc(String desc) {
			this.desc = desc;
		}

	}
}


其中,抽象一个内部类IndexCreator,把构造索引以及如何获取索引字段值的工作抛给用户

</pre><p></p><p></p><pre name="code" class="html">/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 通用位图搜索工具类
 *
 * @author HouKangxi
 *
 */
public final class BitmapSearcher {
	/**
	 * 对象list,只读。
	 */
	@SuppressWarnings("rawtypes")
	private final List beansList;
	/**
	 * 供搜索用的索引: K: index, value:<fieldVal--bits>
	 */
	private Map<Object, Map<Object, BigInteger>> indexMap;
	/**
	 * 索引构造器
	 */
	@SuppressWarnings("rawtypes")
	private final IndexCreator indexCreator;

	/**
	 * 索引构造器
	 *
	 *
	 *
	 * @param <T,<span style="font-family: Arial, Helvetica, sans-serif;">INDEX</span>>
	 */
	public static interface IndexCreator<T, INDEX> {
		/**
		 * 返回一组索引
		 *
		 * @return
		 */
		INDEX[] indexKeys();

		/**
		 * 获取指定索引名对应的字段值
		 *
		 * @param bean
		 *            - list中的对象
		 * @param indexKey
		 *            - 索引名
		 * @return
		 */
		Object[] fieldValue(T bean, INDEX indexKey);
	}

	/**
	 * 构造方法
	 *
	 * @param objList
	 *            - 对象list
	 * @param ic
	 *            - 索引构造器
	 */
	public <T, INDEX> BitmapSearcher(List<T> objList, IndexCreator<T, INDEX> ic) {
		indexCreator = ic;
		if (objList != null && objList.size() > 0) {
			beansList = Collections.unmodifiableList(objList);
			createAllIndex();
		} else {
			beansList = Collections.emptyList();
		}
	}

	Map<Object, BigInteger> getBitmap(Object key) {
		return indexMap.get(key);
	}

	/**
	 * 查询一个结果
	 *
	 * @param <T>
	 *
	 * @param criteria
	 *            - 查询条件
	 * @param sorter
	 *            - 指定的排序器
	 * @return
	 */
	public <T> T findOne(Criteria criteria, Comparator<T> sorter) {
		List<T> list = find(criteria);
		if (list == null || list.isEmpty()) {
			return null;
		}
		if (sorter != null)
			Collections.sort(list, sorter);
		// 取出第一个
		return list.get(0);
	}

	/**
	 * 查询 list
	 *
	 * @param <T>
	 * @param criteria
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> find(Criteria criteria) {
		if (beansList == null || beansList.isEmpty()) {
			return null;
		}
		BigInteger bInt = criteria.proc(this, null);
		if (bInt == null) {
			return null;
		}
		ArrayList<Integer> indexes = new ArrayList<Integer>();
		int idx;
		while ((idx = bInt.getLowestSetBit()) >= 0) {
			indexes.add(idx);
			// 将当前值与它减去1的值做&运算,正好下次可以得到最右边的 1
			bInt = bInt.and(bInt.subtract(BigInteger.ONE));
		}
		// 序号 indexes 天然是从小到大排列,且不会重复,因为每次都是找最右边的1
		if (indexes.isEmpty()) {
			return null;
		}
		@SuppressWarnings("rawtypes")
		ArrayList rslist = new ArrayList(indexes.size());
                for (int i : indexes) {
		     rslist.add(beansList.get(i));
		}
		return rslist;
	}

	private void createAllIndex() {
		Map<Object, Map<Object, byte[]>> t_Index = new HashMap<Object, Map<Object, byte[]>>();
		Object[] keyNames = indexCreator.indexKeys();
		for (int i = 0; i < keyNames.length; i++) {
			t_Index.put(keyNames[i], new HashMap<Object, byte[]>());
		}
		int i = 0;
		final int SUM = beansList.size();
		for (Object o : beansList) {
			createIndex(o, t_Index, SUM, i, keyNames);
			i++;
		}
		indexMap = new HashMap<Object, Map<Object, BigInteger>>(t_Index.size());
		bytes2BigInteger(t_Index, indexMap);
	}

	private void createIndex(Object o, Map<Object, Map<Object, byte[]>> t_Index, final int SUM, final int index,
			Object[] indexes) {
		if (o == null) {
			return;
		}
		final int bytesLen = (SUM >> 3) + 1;
		final int byteIndex = bytesLen - 1 - (index >> 3);
		final int value = 1 << (index % 8);

		for (int i = 0; i < indexes.length; i++) {
			Object key = indexes[i];
			@SuppressWarnings("unchecked")
			Object fieldValues[] = indexCreator.fieldValue(o, key);
			if (fieldValues == null) {
				continue;
			}
			Map<Object, byte[]> bIntMap = t_Index.get(key);
			for (Object fieldValue : fieldValues) {
				if (fieldValue != null) {
					byte[] bInt = bIntMap.get(fieldValue);
					if (bInt == null) {
						bIntMap.put(fieldValue, bInt = new byte[bytesLen]);
					}
					bInt[byteIndex] |= value;
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	private void bytes2BigInteger(Map<Object, Map<Object, byte[]>> t_Index,
			Map<Object, Map<Object, BigInteger>> bigInts) {
		for (Map.Entry<Object, Map<Object, byte[]>> entry : t_Index.entrySet()) {
			Object key = entry.getKey();
			Map<Object, byte[]> value = entry.getValue();
			if (value == null || value.isEmpty()) {
				continue;
			}
			@SuppressWarnings("rawtypes")
			Map ov = value;
			for (Map.Entry<Object, byte[]> v : value.entrySet()) {
				ov.put(v.getKey(), new BigInteger(v.getValue()));
			}
			bigInts.put(key, ov);
		}
	}

}

下面是Criteria的一些实现类:

/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;

/**
 * 通用查询约束
 *
 * @author houkangxi
 *
 */
public class Criteria {
	protected Object key;

	protected List<Criteria> chain;
	private Criteria prev = this;

	public Criteria() {
		chain = new LinkedList<Criteria>();
	}

	public Criteria(Object key) {
		this();
		this.key = key;
	}

	Criteria(int noInitChain) {
	}

	public static Criteria where(Object key) {
		return new Criteria(key);
	}

	private Criteria addToChain(Criteria c) {
		prev = c;
		chain.add(c);
		return this;
	}

	public Criteria is(Object val) {
		prev.addToChain(new CriteriaOpIs(prev.key, val));
		return this;
	}

	public Criteria ne(Object val) {
		prev.addToChain(new CriteriaOpNot(prev.key, val));
		return this;
	}

	public Criteria in(Object... val) {
		prev.addToChain(new CriteriaOpIn(prev.key, val));
		return this;
	}

	public Criteria and(String key) {
		return addToChain(new CriteriaOpAnd(key));
	}

	public Criteria or(String key) {
		return addToChain(new CriteriaOpOr(key));
	}

	public Criteria andOperator(Criteria... o) {
		return addToChain(new CriteriaOpAnd(o));
	}

	public Criteria orOperator(Criteria... o) {
		return addToChain(new CriteriaOpOr(o));
	}

	BigInteger proc(BitmapSearcher sea, BigInteger prev) {
		if (chain == null) {
			return null;
		}
		BigInteger rs = prev;
		for (Criteria c : chain) {
			rs = c.proc(sea, rs);
		}
		return rs;
	}

	@Override
	public String toString() {
		return getClass().getSimpleName() + "@key=" + key;
	}
}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.Arrays;

/**
 * @author houkangxi
 *
 */
abstract class CriteriaChain extends Criteria {

	CriteriaChain(String key) {
		super(key);
	}

	CriteriaChain(Criteria[] list) {
		super(0);
		this.chain = Arrays.asList(list);
	}

	protected abstract BigInteger op(BigInteger o1, BigInteger o2);

	@Override
	protected final BigInteger proc(BitmapSearcher sea, BigInteger prev) {
		if (chain == null || chain.isEmpty()) {
			return null;
		}
		BigInteger h = chain.get(0).proc(sea, null);
		for (int i = 1; i < chain.size() && h != null; i++) {
			h = op(h, chain.get(i).proc(sea, h));
		}
		if (prev == null) {
			return h;
		} else if (h != null) {
			return op(prev, h);
		}
		return null;
	}

}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;

/**
 * And (且)查询
 * @author houkangxi
 *
 */
class CriteriaOpAnd extends CriteriaChain {

	CriteriaOpAnd(String key) {
		super(key);
	}

	CriteriaOpAnd(Criteria[] list) {
		super(list);
	}

	@Override
	protected BigInteger op(BigInteger o1, BigInteger o2) {
		if (o2 == null || o1 == null) {
			return null;
		}
		return o1.and(o2);
	}
}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.Map;

/**
 * IN 查询
 * @author houkangxi
 *
 */
class CriteriaOpIn extends Criteria {
	Object[] colls;

	CriteriaOpIn(Object key, Object[] colls) {
		super(key);
		this.colls = colls;
	}

	@Override
	protected BigInteger proc(BitmapSearcher sea, BigInteger prev) {
		Map<Object, BigInteger> bitmap = sea.getBitmap(key);
		if (bitmap == null || colls == null) {
			return null;
		}
		BigInteger bit = null;
		for (int i = 0; i < colls.length; i++) {
			Object val = colls[i];
			BigInteger I = bitmap.get(val);
			if (I != null) {
				bit = bit == null ? I : bit.or(I);
			}
		}
		return bit;
	}
}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.Map;

/**
 * Is查询
 *
 * @author houkangxi
 *
 */
class CriteriaOpIs extends Criteria {
	private Object ov;

	CriteriaOpIs(Object k, Object ov) {
		super(k);
		this.ov = ov;
	}

	@Override
	protected BigInteger proc(BitmapSearcher sea, BigInteger prev) {
		Map<Object, BigInteger> bimap = sea.getBitmap(key);
		if (bimap == null) {
			return null;
		}
		return bimap.get(ov);
	}
}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;
import java.util.Map;

/**
 * NOT (非)查询
 * @author houkangxi
 *
 */
class CriteriaOpNot extends Criteria {
	private Object ov;

	CriteriaOpNot(Object k, Object ov) {
		super(k);
		this.ov = ov;
	}

	@Override
	protected BigInteger proc(BitmapSearcher sea, BigInteger prev) {
		Map<Object, BigInteger> bimap = sea.getBitmap(key);
		if (bimap == null) {
			return null;
		}
		BigInteger b = bimap.get(ov);
		if (b == null) {
			return null;
		}
		return b.not();
	}
}
/**
 *
 */
package bitmapSearch;

import java.math.BigInteger;

/**
 * Or (或)查询
 * @author houkangxi
 *
 */
class CriteriaOpOr extends CriteriaChain {

	CriteriaOpOr(String k) {
		super(k);
	}

	CriteriaOpOr(Criteria[] list) {
		super(list);
	}

	@Override
	protected BigInteger op(BigInteger o1, BigInteger o2) {
		if (o2 == null) {
			return o1;
		}
		if (o1 == null) {
			return o2;
		}
		return o1.or(o2);
	}
}
				
时间: 2024-11-03 21:45:30

布尔运算--java位图搜索实现的相关文章

Java爬虫搜索原理实现

permike 原文 Java爬虫搜索原理实现 没事做,又研究了一下爬虫搜索,两三天时间总算是把原理闹的差不多了,基本实现了爬虫搜索的原理,本次实现还是俩程序,分别是按广度优先和深度优先完成的,广度优先没啥问题,深度优先请慎用,有极大的概率会造成死循环情况,下面深度优先的测试网站就造成了死循环....好吧,我承认是我人品不太好...下面有请代码君出场~~~~~~~~~~~~~~~ 1.广度优先 [java] view plaincopy /** * 完成广度优先搜索 */ package imp

Java:搜索特定后缀名的文件

写了个Java程序,以特定后缀名为条件,在指定目录内递归搜索文件,再生成文件列表. 1 import java.io.File; 2 import java.util.ArrayList; 3 import java.util.List; 4  5 public class FileListGenerator { 6  7     // FileList类可寻找指定路径下所有指定文件后缀名的文件,并生成文件列表 - List<File> 8  9     FileListGenerator(F

数字全排列 java深度优先搜索

1 import java.util.Scanner; 2 import java.util.ArrayList; 3 public class Quanpailie{ 4 5 public int a[] ; 6 public int book[] ; 7 public int n; 8 public int step=1; 9 public static void main(String[] args) { 10 Quanpailie he=new Quanpailie(); 11 he.g

Java递归搜索指定文件夹下的匹配文件

import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Queue; /** * @author tiwson 2010-06-02 * */ public class FileSearcher { /** * 递归查找文件 * @param baseDirName 查找的文件夹路径 * @param targetFileName 需要查找的文件名 * @param file

java实现搜索附近地点或人的功能

前言 当前大多数app都有查找附近的功能, 简单的有查找周围的运动场馆, 复杂的有滴滴, 摩拜查找周围的车辆. 本文主要阐述查找附近地点的一般实现. 搜索附近的人也是同样的思路. 方案比较 方案1 (性能还不错) 数据库直接存经纬度, 然后计算矩形边界值, 走索引查询 方案2 (还没试过) 将经纬度转换成 一个值, 然后进行比较查询 genhash http://blog.csdn.net/newjueqi/article/details/18989867 方案3 (据说高性能, 性能怎样?待测

Java微博搜索关键字采集

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.UnsupportedEncodingException; import java.net.Mal

[刘阳Java]_eayui-searchbox搜索组件_第6讲

EasyUI中搜索框也是常用的基本组件,可以用到条件搜索中 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <!-- 1.引入jquery.min.js 2.引入jquery.easyui.min.js 3.引入jqueryeasyui的css 4.引入国际化资源文件 EasyUI创建组

Java 布尔运算

章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符串 Java Math Java 布尔运算 Java If - Else Java Switch Java While 循环 Java For 循环 Java Break 与 Continue Java 数组 Java 异常 Java 方法 Java 类与对象 Java 类.对象 Java 类的属性

9个基于Java的搜索引擎

1.Java 全文搜索引擎框架 Lucene 毫无疑问,Lucene是目前最受欢迎的Java全文搜索框架,准确地说,它是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎.Lucene为开发人员提供了相当完整的工具包,可以非常方便地实现强大的全文检索功能.下面有几款搜索引擎框架也是基于Lucene实现的. 官方网站:http://lucene.apache.org/ 2.开源Java搜索引擎Nutch Nutch 是一个开源Java实现的搜索引擎.它提供了我们运行自己的搜索