博主遇到一个问题,要对文章根据用户阅读记录进行去重,但用户阅读记录的文章ID最长可以达到300条,然后在数据库中使用NOT IN语句在查询时对文章进行去重,但是这样操作在记录比较长时,语句执行效率极其低下,
最终博主想到了一个优化策略,在redis中缓存用户阅读的文章ID区间(文章ID是递增方式存入数据库)取代之间对文章ID校验去重的方式进行去重,这时就涉及到对用户的阅读文章ID区间进行求交集的操作,具体求交集思路见代码:
import java.util.*;
public class Main {
public static boolean union(Set<String> set) {
/*求并集 每次将两个可并集的区间合并*/
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String old = it.next();
String[] arr = old.split(",");
Iterator<String> it2 = set.iterator();
while (it2.hasNext()) {
String newstr = it2.next();
String[] arr2 = newstr.split(",");
if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
//标记是否被合并
boolean isRemove = false;
//是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
//如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
arr[1] = arr2[1];
isRemove = true;
}
//如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
arr[0] = arr2[0];
isRemove = true;
}
set.remove(newstr);
if (isRemove) {
//如果区间一的值被改变 修改区间一
set.remove(old);
set.add(arr[0] + "," + arr[1]);
}
return true;
}
}
}
}
return false;
}
public static void main(String[] args) {
/*测试运行效率*/
Random random = new Random();
Set<String> set = new HashSet<String>();
for (int i = 0; i < 100000; i++) {
String insStr = "";
int num = random.nextInt(10000000);
insStr = random.nextInt(num) + "," + num;
set.add(insStr);
}
/*测试准确性*/
set.add("0,15");
set.add("8,15");
set.add("6,7");
set.add("100,150");
set.add("12,85");
set.add("4,10");
set.add("5,16");
System.out.println("0.0 " + new Date().getTime());
while (union(set)) {
}
System.out.println("-.- " + new Date().getTime());
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next().toString());
}
}
优化代码:后期博主需要获取最大值 并对过小的值进行合并整合 故而优化方法如下(该优化弃用) ;
/**
* 合并所有集合 并返回最大值
* @param set
* @param max
* @return
*/
public static Long union(Set<String> set ,Long max) {
/*求并集 每次将两个可并集的区间合并*/
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String old = it.next();
String[] arr = old.split(",");
Iterator<String> it2 = set.iterator();
if (Long.parseLong(arr[1]) > max) {
max = Long.parseLong(arr[1]);
}
while (it2.hasNext()) {
String newstr = it2.next();
String[] arr2 = newstr.split(",");
if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
//标记是否被合并
boolean isRemove = false;
//是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
//如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
arr[1] = arr2[1];
isRemove = true;
}
//如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
arr[0] = arr2[0];
isRemove = true;
}
set.remove(newstr);
if (isRemove) {
//如果区间一的值被改变 修改区间一
set.remove(old);
set.add(arr[0] + "," + arr[1]);
}
max = union(set , max);
return max;
}
}
}
}
return max;
}
博主对上面的代码进行大数据(10W+区间)测试后,发现会出现堆栈错误,递归调用太多,线程池满了;
重新优化代码后 耦合度增加 但减少运算次数
import java.util.*;
public class Main {
/**
* 合并所有集合 并返回最大值
*
* @param set
* @param max
* @return
*/
public static Long union(Set<String> set, Long max) {
/*求并集 每次将两个可并集的区间合并*/
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String old = it.next();
String[] arr = old.split(",");
Iterator<String> it2 = set.iterator();
if (Long.parseLong(arr[1]) > max) {
max = Long.parseLong(arr[1]);
}
while (it2.hasNext()) {
String newstr = it2.next();
String[] arr2 = newstr.split(",");
if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
//标记是否被合并
boolean isRemove = false;
//是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
//如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
arr[1] = arr2[1];
isRemove = true;
}
//如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
arr[0] = arr2[0];
isRemove = true;
}
set.remove(newstr);
if (isRemove) {
//如果区间一的值被改变 修改区间一
set.remove(old);
set.add(arr[0] + "," + arr[1]);
}
// max = union(set , max);
return max;
}
}
}
}
return -1L;
}
public static void removeSmall(Set<String> set) {
Iterator<String> it = set.iterator();
Long min = 0L;
Long max = 0L;
while (it.hasNext()) {
}
}
public static void main(String[] args) {
/*测试运行效率*/
Random random = new Random();
Set<String> set = new HashSet<String>();
for (int i = 0; i < 100000; i++) {
String insStr = "";
int num = random.nextInt(10000000);
insStr = random.nextInt(num) + "," + num;
set.add(insStr);
}
/*测试准确性*/
set.add("0,15");
set.add("8,15");
set.add("6,7");
set.add("100,200");
set.add("12,85");
set.add("4,10");
set.add("5,16");
System.out.println("0.0 " + new Date().getTime());
Long max = 0L;
for(Long tempMax=max; (tempMax = union(set, max)) >= 0 ; )
{
max = tempMax;
}
System.out.println("MAX = " + max);
System.out.println("-.- " + new Date().getTime());
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next().toString());
}
}
}