集合去重,筛选交集、差集(并集)【工具类】

3 篇文章 0 订阅
3 篇文章 0 订阅

Java

环境

JDK-8
采用双for循环,同时得出交集、差集。不依赖SetMap特性,减少内存浪费。
当交集量较大时,可将diffRight = new ArrayList<>()改为链表diffRight = new LinkedList<>(),以提高remove操作性能。改造方式,自由发挥。

Class

Coll.java

import static java.util.stream.Collectors.toList;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 集合去重
 * @author Liu
 * @date 2022-9-6 16:11:17
 */
public class Coll {

	/**
	 * @see {@link #sieve(Collection, Collection, BiPredicate)}
	 */
	@SuppressWarnings("unlikely-arg-type")
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right) {
		return sieve(left, right, L::equals);
	}
	/**
	 * @see {@link #sieve(Collection, Collection, BiPredicate, BiFunction, Function)}
	 */
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right, BiPredicate<L, R> eq) {
		if (eq == null) {
			return sieve(left, right);
		}
		return sieve(left, right, eq, l -> {});
	}
	/**
	 * @see {@link #sieve(Collection, Collection, BiPredicate, BiFunction, Function)}
	 */
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right,
			BiPredicate<L, R> eq,
			Consumer<L> fit) {
		return sieve(left, right, eq, (l, r) -> fit.accept(l));
	}
	/**
	 * @see {@link #sieve(Collection, Collection, BiPredicate, BiFunction, Function)}
	 */
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right,
			BiPredicate<L, R> eq,
			BiConsumer<L, R> fit) {
		return sieve(left, right, eq, (l, r) -> {
			fit.accept(l, r);
			return true;
		}, Function.identity());
	}
	/**
	 * 筛选(Lambda)
	 * @param left {@link Collection}<{@link L}>:左集;
	 * @param right {@link Collection}<{@link L}>:右集;
	 * @param fit {@link BiConsumer}<{@link L}, {@link L}>:对{@code eq}符合条件的元素操作,并返回{@code boolean}值,表示是否添加到{@link Union#intersect};
	 * @param eqs {@link Function}<{@link L}, ?>[]:多个筛选条件的平铺写法。
	 * 	<i>默认:{@link Object#equals(Object)}</i>
	 * 	<ul>例如:
	 * 		<li>{@link java.time.LocalDate}::getYear, {@link java.time.LocalDate}::getMonth</li>
	 * 	</ul>;
	 * @return {@link Union}<{@link L}, {@link L}>:筛选结果;
	 * @author Liu
	 * @date 2022-9-6 16:11:17
	 * @see {@link #sieve(Collection, Collection, BiPredicate, BiFunction, Function)}
	 */
	@SafeVarargs
	public static <L> Union<L, L> sieve(Collection<L> left, Collection<L> right,
			BiConsumer<L, L> fit,
			Function<L, ?>... eqs) {
		return sieve(left, right, (l, r) -> {
			for (int i = 0; i < eqs.length; i++) {
				Function<L, ?> eq = eqs[i];
				Object lv = eq.apply(l);
				Object rv = eq.apply(r);
				if (!lv.equals(rv)) {
					return false;
				}
			}
			return true;
		}, (l, r) -> {
			fit.accept(l, r);
			return true;
		}, Function.identity());
	}
	/**
	 * @see {@link #sieve(Collection, Collection, BiPredicate, BiFunction, Function)}
	 */
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right,
			BiPredicate<L, R> eq,
			Predicate<L> fit) {
		return sieve(left, right, eq, (l, r) -> fit.test(l), Function.identity());
	}
	/**
	 * 筛选(Lambda)
	 * @param left {@link Collection}<{@link L}>:左集;
	 * @param right {@link Collection}<{@link R}>:右集;
	 * @param eq {@link BiPredicate}<{@link L}, {@link R}>:筛选条件,判断相同的条件。<i>默认:{@link Object#equals(Object)}</i>;
	 * @param fit {@link BiPredicate}<{@link L}, {@link R}>:对{@code eq}符合条件的元素操作,并返回{@code boolean}值,表示是否添加到{@link Union#intersect};
	 * @param diffLeft {@link Function}<{@link L}, {@link L}>:对{@link Union#diffLeft}左差集元素操作;
	 * @return {@link Union}<{@link L}, {@link R}>:筛选结果;
	 * @author Liu
	 * @date 2022-9-6 16:11:17
	 * @see {@link #sieve(Collection, Collection, Sieve)}
	 */
	@SuppressWarnings("unlikely-arg-type")
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right,
			BiPredicate<L, R> eq,
			BiPredicate<L, R> fit,
			Function<L, L> diffLeft) {
		if (eq == null) {
			eq = L::equals;
		}
		BiPredicate<L, R> equal = eq;
		return sieve(left, right, new Sieve<L, R>() {
			@Override
			public boolean eq(L left, R right) {
				return equal.test(left, right);
			}
			@Override
			public boolean fit(L left, R right, int i) {
				return fit.test(left, right);
			}
			@Override
			public L diffLeft(L left) {
				return diffLeft.apply(left);
			}
		});
	}

	/**
	 * 筛选
	 * @param left {@link Collection}<{@link L}>:左集;
	 * @param right {@link Collection}<{@link R}>:右集;
	 * @param sieve {@link Sieve}<{@link L}, {@link R}>:筛选器;
	 * @return {@link Union}<{@link L}, {@link R}>:筛选结果;
	 * @author Liu
	 * @date 2022-9-6 16:11:17
	 */
	public static <L, R> Union<L, R> sieve(Collection<L> left, Collection<R> right, Sieve<L, R> sieve) {
		Union<L, R> union = new Union<>();
		if (left == null || left.isEmpty()) {
			return union.setDiffRight(right);
		}
		if (right == null || right.isEmpty()) {
			return union.setDiffLeft(left);
		}
		List<R> diffRight = new ArrayList<>(right);
		List<L> diffLeft = new ArrayList<>(left.size());
		union.setDiffRight(diffRight).setDiffLeft(diffLeft);
		@SuppressWarnings("unchecked")
		Sieve<L, R> si = sieve != null ? sieve : (Sieve<L, R>) Sieve.SIEVE;
		List<L> intersect = left.stream().filter(le -> {
			for (int i = 0; i < diffRight.size(); i++) {
				R ri = diffRight.get(i);
				if (si.eq(le, ri)) {
					return si.fit(le, diffRight.remove(i--), i);
				}
			}
			diffLeft.add(si.diffLeft(le));
			return false;
		}).collect(toList());
		return union.setIntersect(intersect);
	}

	/**
	 * 汇集结果
	 * @param <L>:左集类型;
	 * @param <R>:右集类型;
	 * @author Liu
	 * @date 2022-9-6 16:11:17
	 */
	public static class Union<L, R> implements Serializable {
		private static final long serialVersionUID = -5870745947717525652L;

		/**
		 * 交集
		 */
		private Collection<L> intersect = Collections.emptyList();
		/**
		 * 左差集
		 */
		private Collection<L> diffLeft = Collections.emptyList();
		/**
		 * 右差集
		 */
		private Collection<R> diffRight = Collections.emptyList();

		public Union() {
			super();
		}
		public Union(Collection<L> intersect, Collection<L> diffLeft, Collection<R> diffRight) {
			this();
			this.setIntersect(intersect).setDiffLeft(diffLeft).setDiffRight(diffRight);
		}

		/**
		 * 交集
		 */
		public Collection<L> getIntersect() {
			return intersect;
		}
		public Union<L, R> setIntersect(Collection<L> intersect) {
			this.intersect = intersect != null ? intersect : this.intersect;
			return this;
		}
		/**
		 * 左差集
		 */
		public Collection<L> getDiffLeft() {
			return diffLeft;
		}
		public Union<L, R> setDiffLeft(Collection<L> diffLeft) {
			this.diffLeft = diffLeft != null ? diffLeft : this.diffLeft;
			return this;
		}
		/**
		 * 右差集
		 */
		public Collection<R> getDiffRight() {
			return diffRight;
		}
		public Union<L, R> setDiffRight(Collection<R> diffRight) {
			this.diffRight = diffRight != null ? diffRight : this.diffRight;
			return this;
		}

		@Override
		public String toString() {
			return "{intersect: " + intersect + ",\r\ndiffLeft: " + diffLeft + ",\r\ndiffRight: " + diffRight + "}";
		}
	}

	/**
	 * 筛选功能器
	 * @author Liu
	 * @date 2022-9-6 17:20:23
	 */
	public static interface Sieve<L, R> {
		Sieve<Object, Object> SIEVE = new Sieve<Object, Object>() {};
		/**
		 * 筛选条件,判断为相同的条件
		 * @param left {@link L}:左集元素;
		 * @param right {@link R}:右集元素;
		 * @return {@link Boolean}:两元素是否匹配;
		 * @see {@link Object#equals(Object)}
		 */
		@SuppressWarnings("unlikely-arg-type")
		default boolean eq(L left, R right) {
			return left.equals(right);
		}
		/**
		 * 符合元素
		 * @param left {@link L}:左集匹配元素;
		 * @param right {@link R}:右集被移除的匹配元素;
		 * @param i int:索引;
		 * @return {@link Boolean}:是否加入交集;
		 */
		default boolean fit(L left, R right, int i) {
			return true;
		}
		/**
		 * 交集元素
		 */
//		default L intersect(L intersect) {
//			return intersect;
//		}
		/**
		 * 右差集元素
		 */
//		default R diffRight(R diffRight) {
//			return diffRight;
//		}
		/**
		 * 左差集元素
		 */
		default L diffLeft(L left) {
			return left;
		}
	}
}

并集 = 交集 + 左差集 + 右差集

测试示例

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;

/**
 * 测试去重
 * @author Liu
 * @date 2022-9-6 16:11:17
 */
public class Test {
	public static void main(String[] args) {
		List<LocalDate> dtl = Arrays.asList(
				LocalDate.of(2022, 10, 1),
				LocalDate.of(2022, 9, 2),
				LocalDate.of(2022, 9, 6),
				LocalDate.of(2022, 9, 28)
			);
		List<LocalDate> dtr = Arrays.asList(
				LocalDate.of(2022, 9, 1),
				LocalDate.of(2022, 9, 2),
				LocalDate.of(2022, 9, 6)
			);
		/* 以 右集合 为基准数据 */
		Union<LocalDate, LocalDate> dt = Coll.sieve(dtl, dtr);
		System.out.println("相同 - Update:\t" + dt.getIntersect());
		System.out.println("新增 - Insert:\t" + dt.getDiffLeft());
		System.out.println("删除 - Delete:\t" + dt.getDiffRight());
		System.out.println();

		List<LocalTime> tml = Arrays.asList(
				LocalTime.of(0, 59, 59),
				LocalTime.of(0, 0),
				LocalTime.of(0, 0, 1),
				LocalTime.of(1, 0)
			);
		List<LocalTime> tmr = Arrays.asList(
				LocalTime.of(0, 0),
				LocalTime.of(0, 0, 1),
				LocalTime.of(0, 6)
			);
		LocalDate atDate = LocalDate.of(2022, 10, 1);
		List<LocalDateTime> dtms = new java.util.ArrayList<>(3);
		BiConsumer<LocalTime, LocalTime> fit = (l, r) -> dtms.add(l.atDate(atDate));	// 对相同的元素,做其它操作
		Union<LocalTime, LocalTime> tm = Coll.sieve(tml, tmr, fit, LocalTime::getHour, LocalTime::getMinute);	// 时分相等,则视为相同
		System.out.println("相同 - Update:\t" + tm.getIntersect());
		System.out.println("新增 - Insert:\t" + tm.getDiffLeft());
		System.out.println("删除 - Delete:\t" + tm.getDiffRight());
		System.out.println(dtms);
		System.out.println();

		List<LocalDateTime> dtml = Arrays.asList(
				LocalDateTime.of(LocalDate.of(2022, 10, 1), LocalTime.MIN),
				LocalDateTime.of(LocalDate.of(2022, 9, 2), LocalTime.MIN),
				LocalDateTime.of(LocalDate.of(2020, 9, 6), LocalTime.MIN),
				LocalDateTime.of(LocalDate.of(2022, 9, 28), LocalTime.MIN)
			);
		List<LocalDateTime> dtmr = Arrays.asList(
				LocalDateTime.of(LocalDate.of(2022, 9, 1), LocalTime.MIN),
				LocalDateTime.of(LocalDate.of(2022, 9, 2), LocalTime.MIN),
				LocalDateTime.of(LocalDate.of(2020, 9, 6), LocalTime.MIN)
			);
		Union<LocalDateTime, LocalDateTime> dtUn = Coll.sieve(dtml, dtmr,
				(l, r) -> l.toLocalDate().equals(r.toLocalDate()),
				(l, r) -> !l.toLocalDate().isLeapYear(),	// 闰年不添加到并集
				l -> l.plusHours(8)
			);
		System.out.println("相同 - Update:\t" + dtUn.getIntersect());
		System.out.println("新增 - Insert:\t" + dtUn.getDiffLeft());
		System.out.println("删除 - Delete:\t" + dtUn.getDiffRight());
		System.out.println();

		List<LocalDate> datel = Arrays.asList(
				LocalDate.of(2022, 10, 1),
				LocalDate.of(2022, 9, 2),
				LocalDate.of(2022, 9, 6),
				LocalDate.of(2022, 9, 28)
			);
		List<LocalTime> timer = Arrays.asList(
				LocalTime.of(0, 0),
				LocalTime.of(0, 0, 1),
				LocalTime.of(0, 6)
			);
		Union<LocalDate, LocalTime> dtm = Coll.sieve(datel, timer, (l, r) -> l.getDayOfMonth() == r.getSecond());
		System.out.println("相同 - Update:\t" + dtm.getIntersect());
		System.out.println("新增 - Insert:\t" + dtm.getDiffLeft());
		System.out.println("删除 - Delete:\t" + dtm.getDiffRight());
		System.out.println();
	}
}

JavaScript、NodeJS

环境

需支持ES6

代码

/**
 * 筛选功能器
 */
function Sieve() {}
(function () {
	let sieve;
	/**
	 * 判断相同的条件
	 * @return {@link boolean}
	 */
	Sieve.prototype.eq = (left, right) => left == right;
	/**
	 * 是否将符合的元素加入交集
	 * @return {@link boolean}:true - 加入。
	 */
	Sieve.prototype.fit = (left, right, i) => true;
	/**
	 * 左差集元素
	 * @return {@link Object}:转换结果。
	 */
	Sieve.prototype.diffLeft = left => left;
	/**
	 * 筛选
	 * @param left {@link Array}:左集;
	 * @param right {@link Array}:右集;
	 * @return {@link Union}:筛选结果;
	 */
	Sieve.prototype.sieve = function (left, right) {
		let union = new Union();
		if (!left) {
			return union.setDiffRight(right);
		}
		if (!right) {
			return union.setDiffLeft(left);
		}
		let diffRight = [...right];
		let diffLeft = [];
		union.setDiffRight(diffRight).setDiffLeft(diffLeft);
		const intersect = left.filter(le => {
			for (let i = 0; i < diffRight.length; i++) {
				if (this.eq(le, diffRight[i])) {
					return this.fit(le, diffRight.splice(i, 1)[0], i);
				}
			}
			diffLeft.push(this.diffLeft(le));
			return false;
		})
		return union.setIntersect(intersect);
	}
	/**
	 * 单例
	 */
	Sieve.newInstance = () => (sieve = sieve || new Sieve());
})()

/**
 * 汇集结果
 * @param intersect {@link Array}:交集;
 * @param diffLeft {@link Array}:左差集;
 * @param diffRight {@link Array}:右差集;
 */
function Union(intersect, diffLeft, diffRight) {
	return this.setIntersect(intersect).setDiffLeft(diffLeft).setDiffRight(diffRight);
}
(function(cla) {
	cla.prototype.getIntersect = function () {
		return (this.intersect ||= [])
	}
	cla.prototype.getDiffLeft = function () {
		return (this.diffLeft ||= [])
	}
	cla.prototype.getDiffRight = function () {
		return (this.diffRight ||= [])
	}

	cla.prototype.setIntersect = function (intersect) {
		this.intersect = intersect;
		return this;
	}
	cla.prototype.setDiffLeft = function (diffLeft) {
		this.diffLeft = diffLeft;
		return this;
	}
	cla.prototype.setDiffRight = function (diffRight) {
		this.diffRight = diffRight;
		return this;
	}
})(Union)

测试示例

let le = [ 0, 3, 5, '6', 7, 9 ];
let ri = [ 2, false, 6, 8, 9 ];
let un = Sieve.newInstance().sieve(le, ri);
console.log(un);
/*
intersect: [ 0, '6', 9 ],
diffLeft: [ 3, 5, 7 ],
diffRight: [ 2, 8 ]
*/

使用过程中,如出现bug,或有其它优化建议,欢迎在此文章“评论区”留言讨论,并留下您的邮箱,以便改正后及时通知您。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值