问题
如今,互联网技术成熟,越来越多的趋势趋向于去中心化、分布式化、流媒体计算,很多以前在数据库方面做的事情都放在Java方面。 如果今天数据库字段没有索引,该如何根据该字段重新定位? 用Java做的话大家都一致,怎么办?
解答
突然想起以前写list去重的文章,试着找了一下。 方法是重写列表中对象的hashcode和equals方法,使其落入HashSet中,然后将其检索。 这是最初学Java时像字典一样背诵写的答案。 例如在面试中,我遇到过一个自称做了三年Java的人。 虽然可以背诵Set和HashMap的区别,但是问怎么实现就不知道了。 也就是说,初学者只背特性。 但是,真正在项目中使用的时候,需要确认是否真的是这样。 背书没有用,所以只能相信结果。 我需要知道HashSet是怎么帮助我的。 换个想法,可以不使用HashSet而去重物吗? 最简单、最直接的方法不是每次都拿着和历史数据进行比较,而是如果一切都不一样的话,插入到团队的最后。 HashSet只是加快了这个过程。
首先,指示要排序的用户
@Data
@Builder
@AllArgsConstructor
公共类用户{
私有integer id;
私有字符串名称;
}
List users=Lists.newArrayList (
新用户(1,' a )、
新用户(1,' b )、
新用户(2,' b '表示,
新用户(1,' a ' );
目标可以通过检索id不重复的用户,给出防止剥皮的规则,任意检索id唯一的数据即可,不局限于在id相同的情况下计算哪个。
用最直观的方法
此方法是用空列表保存扫描后的数据。
@Test
公共语音dis1() {
列表结果=new linked list (;
for (用户:用户) {
boolean b=result.stream ().any match (u-u.getid ).equals(user.getid ) );
if (! b ) {
result.add (用户;
}
}
system.out.println(result );
}
使用HashSet
背诵特性的人知道HashSet会变重,但怎么会变重呢? 再深入一点的背诵依据hashcode和equals方法。 那么是怎么根据这两个做的呢? 没有看过源代码的人不能继续下去。 面试也到此结束。
实际上,HashSet是通过HashMap实现的。 (当我没有看到源代码时,我直观地认为HashMap中的key是由HashSet实现的,但正好相反。 在此不展开记述,只要看HashSet的结构方法和add方法就可以理解。
公共散列()。
map=new HashMap (;
}
//*
*很明显,如果存在则返回false,如果不存在则返回true
*/
publicbooleanadd{
returnmap.put(e,PRESENT )==null;
}
由此可见,混叠的重复是通过混叠来实现的,混叠的实现完全依赖于混叠和质量的方法。 这样就完全明白了。 要使用HashSet,你必须看到自己的这两种方法。
在这个主题中,需要根据id来消除重量。 那么,我们比较的依据是id。 修改如下。
@Override
publicbooleanequals (对象) {
if(this==o ) {
返回真;
}
if(o==null||getclass (!=o.getClass () ) }
返回假;
}
用户=(用户) o;
返回对象. equals (id,user.id );
}
@Override
公共int hashcode (
returnobjects.hash(id;
}
//hashcode
结果=31 *结果(element==null? 0 : element.hashCode ();
其中,Objects调用了Arrays的hashcode,内容如上。 乘以31等于x5
-x。最终实现如下:
@Test
public void dis2() {
Set result = new HashSet<>(users);
System.out.println(result);
}
使用Java的Stream去重
回到最初的问题,之所以提这个问题是因为想要将数据库侧去重拿到Java端,那么数据量可能比较大,比如10w条。对于大数据,采用Stream相关函数是最简单的了。正好Stream也提供了distinct函数。那么应该怎么用呢?
users.parallelStream().distinct().forEach(System.out::println);
没看到用lambda当作参数,也就是没有提供自定义条件。幸好Javadoc标注了去重标准:
Returns a stream consisting of the distinct elements
(according to {@link Object#equals(Object)}) of this stream.
我们知道,也必须背过这样一个准则:equals返回true的时候,hashcode的返回值必须相同. 这个在背的时候略微有些逻辑混乱,但只要了解了HashMap的实现方式就不会觉得拗口了。HashMap先根据hashcode方法定位,再比较equals方法。
所以,要使用distinct来实现去重,必须重写hashcode和equals方法,除非你使用默认的。
那么,究竟为啥要这么做?点进去看一眼实现。
Node reduce(PipelineHelper helper, Spliterator spliterator) {
// If the stream is SORTED then it should also be ORDERED so the following will also
// preserve the sort order
TerminalOp> reduceOp
= ReduceOps.>makeRef(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll);
return Nodes.node(reduceOp.evaluateParallel(helper, spliterator));
}
内部是用reduce实现的啊,想到reduce,瞬间想到一种自己实现distinctBykey的方法。我只要用reduce,计算部分就是把Stream的元素拿出来和我自己内置的一个HashMap比较,有则跳过,没有则放进去。其实,思路还是最开始的那个最直白的方法。
@Test
public void dis3() {
users.parallelStream().filter(distinctByKey(User::getId))
.forEach(System.out::println);
}
public static Predicate distinctByKey(Function super T, ?> keyExtractor) {
Set seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
当然,如果是并行stream,则取出来的不一定是第一个,而是随机的。
上述方法是至今发现最好的,无侵入性的。但如果非要用distinct。只能像HashSet那个方法一样重写hashcode和equals。
小结
会不会用这些东西,你只能去自己练习过,不然到了真正要用的时候很难一下子就拿出来,不然就冒险用。而若真的想大胆使用,了解规则和实现原理也是必须的。比如,LinkedHashSet和HashSet的实现有何不同。
附上贼简单的LinkedHashSet源码:
public class LinkedHashSet
extends HashSet
implements Set, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2851667679971038690L;
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
public LinkedHashSet() {
super(16, .75f, true);
}
public LinkedHashSet(Collection extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
@Override
public Spliterator spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
}
}
补充:
Java中List集合去除重复数据的方法
1. 循环list中的所有元素然后删除重复
public static List removeDuplicate(List list) {
for ( int i = 0 ; i < list.size() - 1 ; i ++ ) {
for ( int j = list.size() - 1 ; j > i; j -- ) {
if (list.get(j).equals(list.get(i))) {
list.remove(j);
}
}
}
return list;
}
2. 通过HashSet踢除重复元素
public static List removeDuplicate(List list) {
HashSet h = new HashSet(list);
list.clear();
list.addAll(h);
return list;
}
3. 删除ArrayList中重复元素,保持顺序
// 删除ArrayList中重复元素,保持顺序
public static void removeDuplicateWithOrder(List list) {
Set set = new HashSet();
List newList = new ArrayList();
for (Iterator iter = list.iterator(); iter.hasNext();) {
Object element = iter.next();
if (set.add(element))
newList.add(element);
}
list.clear();
list.addAll(newList);
System.out.println( " remove duplicate " + list);
}
4.把list里的对象遍历一遍,用list.contain(),如果不存在就放入到另外一个list集合中
public static List removeDuplicate(List list){
List listTemp = new ArrayList();
for(int i=0;i
if(!listTemp.contains(list.get(i))){
listTemp.add(list.get(i));
}
}
return listTemp;
}