在做爆炸特效的过程中遇到一个问题, 虽然这个特效已经绕开这个问题解决了, 但是原问题还想得到解决, 下面是问题描述:
我提取了一个图像的像素数组, 比方百度logo, 看起来好像只有红白蓝几种颜色, 想当然用hashset过滤一下就能出来这几个色值, 奈何过滤后hashset的size仍有688(原数组length=78570), 分析发现, 这些颜色的色差很小, 比如-1762066544, -34209912, -17629308, -17629309, -17760386, -18415766, -18808991, -18808988, -18808981, -1495401354, 1557723508, 449770879, 1070000989, -290532022, -1065312460, 290150957, 41975936; 所以问题产生了, 怎么从这688个数中, 将相似的用一个值代替, 比如-1999, -2001, -2002随便取一个-2001来代替这三个, -2104, -2106, -2107随便取一个2106来代替三个, 以减小数组, 我只想让用户知道这里面有红白蓝这几种颜色, 至于是#FFFFFF还是#FFFFFE用户看不出来, 留一个就行了, 我的解决方案是:
(1)封装一个类似Integer的类RoundInteger, 将这688元素放入HashSet<RoundInteger>中, 关键是里面覆写hashCode(), 将-1999四舍五入为-2000, 将-2001也四舍五入为-2000, 这样出来了一部分相似性, 返回了570个色值, 但这种方式对-2104和-2106这种相似性没有作用;
(2)封装一个类似Integer的类TrunkInteger, 将这688元素放入HashSet<TruncateInteger>中,还是覆写hashCode(), 返回这个元素的个位抹零后的值, 这样出来了一部分相似性, 返回了579个色值, 但这种方式对-1999, -2001, -2002这种相似性没有作用;
(3)建一个Hashset<Integer>, 做两次循环, 一次遍历
570色值, 将里面的原值(非四舍五入)放进去, 一次遍历
579个色值, 也是将里面的原值(非截断个位)放进去, 出来的size是590个(竟然比那个还多?), 将这个
570,
579,
590进行比较后取最小的
570;
弄完以后效果有些差, 更令人堪忧的是做了5次循环, 导致耗了219毫秒, 意识到不该去优化, 应该是陷入了一个坑, 所以换了一种方式做像素提取, 但是请问算法比较好的前辈, 这种几千个数组去除相似性的工作如何展开比较好?
Code:
long millsec = System.currentTimeMillis();
HashSet<Integer> hs = new HashSet<Integer>();
int size = arr.length; //原图像像素数组
for(int i = 0; i < size; ++i)
{
hs.add(arr[i]);
}
HashSet<RoundInteger> rihs = new HashSet<RoundInteger>();
for(Integer i : hs)
{
rihs.add(new RoundInteger(i));
}
HashSet<TruncateInteger> tihs = new HashSet<TruncateInteger>();
for(Integer i : hs)
{
tihs.add(new TruncateInteger(i));
}
HashSet<Integer> ihs = new HashSet<Integer>();
for(RoundInteger ri : rihs)
{
ihs.add(ri.get());
}
for(TruncateInteger ti : tihs)
{
ihs.add(ti.get());
}
Log.e("john", ">>>" + (System.currentTimeMillis() - millsec));
private static final class RoundInteger
{
private final int value;
public RoundInteger(int value) {
this.value = value;
}
public int get() {
return value;
}
@Override
public int hashCode() {
if(value == 0)
return 0;
if(value < 0)
{
return -((int)(-value / 10.0f + 0.5f) * 10);
}else{
return (int)(value / 10.0f + 0.5f) * 10;
}
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RoundInteger other = (RoundInteger) obj;
if (hashCode() != other.hashCode())
return false;
return true;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static final class TruncateInteger
{
private final int value;
public TruncateInteger(int value) {
this.value = value;
}
public int get() {
return value;
}
@Override
public int hashCode() {
return (int)(value / 10.0f) * 10;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TruncateInteger other = (TruncateInteger) obj;
if (hashCode() != other.hashCode())
return false;
return true;
}
@Override
public String toString() {
return String.valueOf(value);
}
}