java bitmap?java编写软件工具
大家好,今天小编来为大家解答以下的问题,关于java bitmap,java编写软件工具这个很多人还不知道,现在让我们一起来看看吧!
BitMap原理与实现
比较经典的问题是:在只能够使用2G的内存中,如何完成以下操作:
①:对10亿个不重复的整数进行排序。
②:找出10亿个数字中重复的数字。
无论是排序还是找重复的数字都需要将这10亿个数字加入到内存中在去进行操作,很明显,题目给出的2G内存限制说明了在这样的场景下是不能够将所有数都加入到内存中的
1000000000* 4/(1024* 1024* 1024)= 3.725G
那么这时候就需要用到 BitMap结构了
bitMap使用一个bit为0/1作为map的value来标记一个数字是否存在,而map的key值正是这个数字本身。
相比于一般的数据结构需要用4个byte去存储数值本身,相当于是节省了 4*8:1= 32倍的内存空间
bitMap不一定要用bit数组,可以使用 int,long等等的基本数据类型实现,因为其实质都是在bit位上存数据,用哪种类型只是决定了最终实现出来的BitMap的内置数组中单个元素存放数据的多少
例如:java中的BitSet使用Long数组
BitMap的实现当然少不了位运算,先来明确几个常见位运算,这是实现BitMap的基础:
set(bitIndex):添加操作
1.确定该数处于数组中的哪个元素的位上
int wordIndex= bitIndex>> 5;
因为我用的是int[]实现,所以这里右移 5位(2^5= 32)
2.确定相对于该元素中的位置偏移
int bitPosition= bitIndex&((1<< 5)- 1);
这里相当于是 bitIndex%(1<<5)的取模运算,因为当取模运算的除数是2的次幂,所以可以使用以下的位运算来计算,提升效率(对比HashMap的容量为什么总是2的幂次方的问题,HashMap求下标时也是使用 hash&(n-1))
tips:位运算的优先级是低于+,-等等的,所以要加上括号,防止发生不可描述的错误
3.将该位置1
bits[wordIndex]|= 1<< bitPosition;
相当于是将指定位置处的bit值置1,其他位置保持不变,也就是将以这个bitIndex为key的位置为1
tips:这里是参考了网上的各位大佬的文章,取余+按位或,又对比了下BitSet的源码:
words[wordIndex]|=(1L<< bitIndex);
没有取余操作,直接|,这两个一样吗?答案当然是一样的
举个栗子:
1<< 148== 1<< 20
1L<< 125==1L<< 61
即对于int和long型数据,直接左移其位数相当于是附带了对其的取模操作
总结:使用Bit-map的思想,我们可以将存储空间进行压缩,而且可以对数字进行快速排序、去重和查询的操作。
Bloom Fliter是Bit-map思想的一种扩展,它可以在允许低错误率的场景下,大大地进行空间压缩,是一种拿错误率换取空间的数据结构
当一个元素加入布隆过滤器中的时候,会进行哪些操作:
当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:
然后,一定会出现这样一种情况:不同的字符串可能哈希出来的位置相同(可以适当增加位数组大小或者调整我们的哈希函数来降低概率),因此:布隆过滤器可能会存在误判的情况
总结来说就是:布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
Bloom Filter的应用:常用于解决缓存穿透等场景。
如何高效使用和管理Bitmap
一、图片加载流程
首先,我们谈谈加载图片的流程,项目中的该模块处理流程如下:
1.在UI主线程中,从内存缓存中获取图片,找到后返回。找不到进入下一步;
2.在工作线程中,从磁盘缓存中获取图片,找到即返回并更新内存缓存。找不到进入下一步;
3.在工作线程中,从网络中获取图片,找到即返回并同时更新内存缓存和磁盘缓存。找不到显示默认以提示。
二、内存缓存类(PanoMemCache)
这里使用Android提供的LruCache类,该类保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。当cache已满的时候加入新的item时,在队列尾部的item会被回收。
[java] view plain copy print?
public class PanoMemoryCache{
// LinkedHashMap初始容量
private static final int INITIAL_CAPACITY= 16;
// LinkedHashMap加载因子
private static final int LOAD_FACTOR= 0.75f;
// LinkedHashMap排序模式
private static final boolean ACCESS_ORDER= true;
//软引用缓存
private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;
//硬引用缓存
private static LruCache<String, Bitmap> mLruCache;
public PanoMemoryCache(){
//获取单个进程可用内存的最大值
//方式一:使用ActivityManager服务(计量单位为M)
/*int memClass=((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/
//方式二:使用Runtime类(计量单位为Byte)
final int memClass=(int) Runtime.getRuntime().maxMemory();
//设置为可用内存的1/4(按Byte计算)
final int cacheSize= memClass/ 4;
mLruCache= new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value){
if(value!= null){
//计算存储bitmap所占用的字节数
return value.getRowBytes()* value.getHeight();
} else{
return 0;
}
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue){
if(oldValue!= null){
//当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存
mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
}
}
};
/*
*第一个参数:初始容量(默认16)
*第二个参数:加载因子(默认0.75)
*第三个参数:排序模式(true:按访问次数排序;false:按插入顺序排序)
*/
mSoftCache= new LinkedHashMap<String, SoftReference<Bitmap>>(INITIAL_CAPACITY, LOAD_FACTOR, ACCESS_ORDER){
private static final long serialVersionUID= 7237325113220820312L;
@Override
protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest){
if(size()> SOFT_CACHE_SIZE){
return true;
}
return false;
}
};
}
/**
*从缓存中获取Bitmap
*@param url
*@return bitmap
*/
public Bitmap getBitmapFromMem(String url){
Bitmap bitmap= null;
//先从硬引用缓存中获取
synchronized(mLruCache){
bitmap= mLruCache.get(url);
if(bitmap!= null){
//找到该Bitmap之后,将其移到LinkedHashMap的最前面,保证它在LRU算法中将被最后删除。
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return bitmap;
}
}
//再从软引用缓存中获取
synchronized(mSoftCache){
SoftReference<Bitmap> bitmapReference= mSoftCache.get(url);
if(bitmapReference!= null){
bitmap= bitmapReference.get();
if(bitmap!= null){
//找到该Bitmap之后,将它移到硬引用缓存。并从软引用缓存中删除。
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return bitmap;
} else{
mSoftCache.remove(url);
}
}
}
return null;
}
/**
*添加Bitmap到内存缓存
*@param url
*@param bitmap
*/
public void addBitmapToCache(String url, Bitmap bitmap){
if(bitmap!= null){
synchronized(mLruCache){
mLruCache.put(url, bitmap);
}
}
}
/**
*清理软引用缓存
*/
public void clearCache(){
mSoftCache.clear();
mSoftCache= null;
}
}
补充一点,由于4.0平台以后对SoftReference类引用的对象调整了回收策略,所以该类中的软引用缓存实际上没什么效果,可以去掉。2.3以前平台建议保留。
三、磁盘缓存类(PanoDiskCache)
五、使用decodeByteArray()还是decodeStream()?
讲到这里,有童鞋可能会问我为什么使用BitmapFactory.decodeByteArray(data, 0, data.length, opts)来创建Bitmap,而非使用BitmapFactory.decodeStream(is, null, opts)。你这样做不是要多写一个静态方法readInputStream()吗?
没错,decodeStream()确实是该使用情景下的首选方法,但是在有些情形下,它会导致图片资源不能即时获取,或者说图片被它偷偷地缓存起来,交还给我们的时间有点长。但是延迟性是致命的,我们等不起。所以在这里选用decodeByteArray()获取,它直接从字节数组中获取,贴近于底层 IO、脱离平台限制、使用起来风险更小。
六、引入缓存机制后获取图片的方法
[java] view plain copy print?
/**
*加载Bitmap
*@param url
*@return
*/
private Bitmap loadBitmap(String url){
//从内存缓存中获取,推荐在主UI线程中进行
Bitmap bitmap= memCache.getBitmapFromMem(url);
if(bitmap== null){
//从文件缓存中获取,推荐在工作线程中进行
bitmap= diskCache.getBitmapFromDisk(url);
if(bitmap== null){
//从网络上获取,不用推荐了吧,地球人都知道~_~
bitmap= PanoUtils.downloadBitmap(this, url);
if(bitmap!= null){
diskCache.addBitmapToCache(bitmap, url);
memCache.addBitmapToCache(url, bitmap);
}
} else{
memCache.addBitmapToCache(url, bitmap);
}
}
return bitmap;
}
七、工作线程池化
有关多线程的切换问题以及在UI线程中执行loadBitmap()方法无效的问题,请参见另一篇博文:使用严苛模式打破Android4.0以上平台应用中UI主线程的“独断专行”。
有关工作线程的处理方式,这里推荐使用定制线程池的方式,核心代码如下:
[java] view plain copy print?
//线程池初始容量
private static final int POOL_SIZE= 4;
private ExecutorService executorService;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//获取当前使用设备的CPU个数
int cpuNums= Runtime.getRuntime().availableProcessors();
//预开启线程池数目
executorService= Executors.newFixedThreadPool(cpuNums* POOL_SIZE);
...
executorService.submit(new Runnable(){
//此处执行一些耗时工作,不要涉及UI工作。如果遇到,直接转交UI主线程
pano.setImage(loadBitmap(url));
});
...
}
我们知道,线程构造也是比较耗资源的。一定要对其进行有效的管理和维护。千万不要随意而行,一张图片的工作线程不搭理也许没什么,当使用场景变为 ListView和GridView时,线程池化工作就显得尤为重要了。Android不是提供了AsyncTask吗?为什么不用它?其实 AsyncTask底层也是靠线程池支持的,它默认分配的线程数是128,是远大于我们定制的executorService。
Java 技巧:用 Java 保存位图文件
如果您在 Microsoft Windows环境中工作那么创建位图文件的功能将为您提供许多方便例如在我的上一个项目中我必须将 Java与 Microsoft Access对接 Java程序允许用户在屏幕上绘图这幅图随后被打印到 Microsoft Access报表中由于 Java不支持 OLE我的唯一选择就是创建该图的一个位图文件并通知 Microsoft Access报表在何处能找到这个位图文件如果您写过向剪贴板发送图像的应用程序则这个技巧可能对您有用尤其是当您将这个信息传递给另一个应用程序时位图文件的格式位图文件格式支持位 RLE(行程长度编码)以及位和位编码因为我们只处理位格式所以下面我们查看一下该文件的结构位图文件分为三个部分我已将它们列在下面第部分位图文件的标头标头包含位图文件的类型大小信息和版面信息结构如下(摘自 C语言结构定义) typedef struct tagBITMAPFILEHEADER{牋燯INT bfType;牋燚WORD bfSize;牋燯INT bfReserved;牋燯INT bfReserved;牋燚WORD bfOffBits;}BITMAPFILEHEADER;下面是对这个清单中的代码元素的说明 bfType指定文件类型其值始终为 BM bfSize指定整个文件的大小(以字节为单位) bfReserved保留必须为 bfReserved保留必须为 bfOffBits指定从 BitmapFileHeader到图像首部的字节偏移量现在您已经明白位图标头的用途就是标识位图文件读取位图文件的每个程序都使用位图标头来进行文件验证第部分位图信息标头随后的标头称为信息标头其中包含图像本身的属性下面说明如何指定 Windows(或更高版本)设备独立位图(DIB)的大小和颜色格式: typedef struct tagBITMAPINFOHEADER{牋牋DWORD biSize;牋牋LONG biWidth;牋牋LONG biHeight;牋牋WORD biPlanes;牋牋WORD biBitCount;牋牋DWORD biCompression;牋牋DWORD biSizeImage;牋牋LONG biXPelsPerMeter;牋牋LONG biYPelsPerMeter;牋牋DWORD biClrUsed;牋牋DWORD biClrImportant;} BITMAPINFOHEADER;以上代码清单的每个元素说明如下 biSize指定 BITMAPINFOHEADER结构所需的字节数 biWidth指定位图的宽度(以象素为单位) biHeight指定位图的高度(以象素为单位) biPlanes指定目标设备的位面数这个成员变量的值必须为 biBitCount指定每个象素的位数其值必须为或 biCompression指定压缩位图的压缩类型在位格式中该变量被设置为 biSizeImage指定图像的大小(以字节为单位)如果位图的格式是 BI_RGB则将此成员变量设置为是有效的 biXPelsPerMeter为位图指定目标设备的水平分辨率(以象素/米为单位)应用程序可用该值从最符合当前设备特征的资源群组中选择一个位图 biYPelsPerMeter为位图指定目标设备的垂直分辨率(以象素/米为单位) biClrUsed指定位图实际所用的颜色表中的颜色索引数如果 biBitCount设为则 biClrUsed指定用来优化 Windows调色板性能的参考颜色表 biClrImportant指定对位图的显示有重要影响的颜色索引数如果此值为则所有颜色都很重要现在已定义了创建图像所需的全部信息第部分图像在位格式中图像中的每个象素都由存储为 BRG的三字节 RGB序列表示每个扫描行都被补足到位为了使这个过程稍复杂一点图像是自底而上存储的即第一个扫描行是图像中的最后一个扫描行下图显示了标头(BITMAPHEADER)和(BITMAPINFOHEADER)以及部分图像各个部分由垂线分隔 D B| E B EC EC| FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF*现在我们开始检视代码现在我们已经知道了位位图文件的结构下面就是您期待已久的内容用来将图像对象写入位图文件的代码 import java awt*;import java io*;import java awt image*;public class BMPFile extends Component{牋//私有常量牋private final static int BITMAPFILEHEADER_SIZE=;牋private final static int BITMAPINFOHEADER_SIZE=;牋//私有变量声明牋//位图文件标头牋private byte bitmapFileHeader []= new byte [ ];牋private byte bfType []={ B M};牋private int bfSize=;牋private int bfReserved=;牋private int bfReserved=;牋private int bfOffBits= BITMAPFILEHEADER_SIZE+ BITMAPINFOHEADER_SIZE;牋//位图信息标头牋private byte bitmapInfoHeader []= new byte [ ];牋private int biSize= BITMAPINFOHEADER_SIZE;牋private int biWidth=;牋private int biHeight=;牋private int biPlanes=;牋private int biBitCount=;牋private int biCompression=;牋private int biSizeImage= x;牋private int biXPelsPerMeter= x;牋private int biYPelsPerMeter= x;牋private int biClrUsed=;牋private int biClrImportant=;牋//位图原始数据牋private int bitmap [];牋//文件部分牋private FileOutputStream fo;牋//缺省构造函数牋public BMPFile(){牋}牋public void saveBitmap(String parFilename Image parImage intparWidth int parHeight){牋牋炉ry{牋牋牋牋fo= new FileOutputStream(parFilename);牋牋牋牋save(parImage parWidth parHeight);牋牋牋牋fo close();牋牋爙牋牋焘atch(Exception saveEx){牋牋牋牋saveEx printStackTrace();牋牋爙牋}牋/*牋? saveMethod是该进程的主方法该方法牋?将调用 convertImage方法以将内存图像转换为牋?字节数组 writeBitmapFileHeader方法创建并写入牋?位图文件标头 writeBitmapInfoHeader创建牋?信息标头 writeBitmap写入图像牋?牋?/牋private void save(Image parImage int parWidth int parHeight){牋牋炉ry{牋牋牋牋convertImage(parImage parWidth parHeight);牋牋牋牋writeBitmapFileHeader();牋牋牋牋writeBitmapInfoHeader();牋牋牋牋writeBitmap();牋牋爙牋牋焘atch(Exception saveEx){牋牋牋牋saveEx printStackTrace();牋牋爙牋}牋/*牋? convertImage将内存图像转换为位图格式(BRG)牋?它还计算位图信息标头所用的某些信息牋?牋?/牋private boolean convertImage(Image parImage int parWidth int parHeight){牋牋爄nt pad;牋牋燽itmap= new int [parWidth* parHeight];牋牋燩ixelGrabber pg= new PixelGrabber(parImage parWidth parHeight牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋牋燽itmap parWidth);牋牋炉ry{牋牋牋牋pg grabPixels();牋牋爙牋牋焘atch(InterruptedException e){牋牋牋牋e printStackTrace();牋牋牋牋return(false);牋牋爙牋牋爌ad=(((parWidth*)%))* parHeight;牋牋燽iSizeImage=((parWidth* parHeight)*)+ pad;牋牋燽fSize= biSizeImage+ BITMAPFILEHEADER_SIZE+BITMAPINFOHEADER_SIZE;牋牋燽iWidth= parWidth;牋牋燽iHeight= parHeight;牋牋爎eturn(true);牋}牋/*牋? writeBitmap将象素捕获器返回的图像转换为牋?所需的格式请记住扫描行在位图文件中是牋?反向存储的!牋?牋?每个扫描行必须补足为个字节牋?/牋private void writeBitmap(){牋牋牋int size;牋牋牋int value;牋牋牋int j;牋牋牋int i;牋牋牋int rowCount;牋牋牋int rowIndex;牋牋牋int lastRowIndex; lishixinzhi/Article/program/Java/JSP/201311/19219
好了,文章到此结束,希望可以帮助到大家。