【解惑】关于HashSet中contains和add方法的疑问——实体类中equals和hashCode方法的重写

时间:2021-07-16 16:14:33
自己写的一个爬虫工具,将一些博客数据爬下来后写入数据库,爬下一定数量的数据之后发现后很多重复,虽然用sql语句写了个去重的模块,但还是希望可以在每爬一个blog之前,可以先检查一下数据库中是否已经存有这个blog的数据。
------------------------------------------------------------------------------------------------------------------------
我的解决方案:
在开始爬网页之前,先将数据库中的所有blog数据存入HashSet中(数据量目前不是太大-1500条左右,存有博客的基本信息和正文),然后爬虫每爬一个blog之前先检查HashSet中是否contains,然后分情况处理。
我将Blog实体类的equals和hashCode方法也重写了,下面贴下代码。
-------------------------------------------------------------------------------------------------------------------------

package com.test.httpclient.page;

import java.util.HashSet;

/**
 * Blog实体类
 * @author Administrator
 *
 */
public class Blog {

private String time;
private String author;
private String title;
private String context;
private String url;

public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}

//通过对比时间、作者、标题来判断blog是否相同
@Override
public boolean equals(Object o) {
if(o == null){
return false;
}
if(o == this){
return true;
}
if(o.getClass() == Blog.class){
Blog blog = (Blog)o;
//这里,检查一下每次抓取的blog 和 从数据库中 取出的  有什么不同。
System.out.println(time + "---------" + blog.time);
System.out.println(author + "---------" + blog.author);
System.out.println(title + "---------" + blog.title);
return time.equals(blog.time) &&
author.equals(blog.author) &&
title.equals(blog.title);
}
return false;
}

//根据时间+作者+标题计算Blog对象的hashCode()返回值
@Override
public int hashCode() {
return (time+author+title).hashCode();
}

public static void main(String[] args){
String context = "2014年的春夏是一个非常多元的春夏,";
Blog blog1 = new Blog();
blog1.setTime("2014-04-25 12:35:00");
blog1.setAuthor("1344360230");
blog1.setTitle("2014春夏色彩妆出来");
blog1.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog1.setContext(context);
Blog blog2 = new Blog();
blog2.setTime("2014-04-25 12:35:00");
blog2.setAuthor("1344360230");
blog2.setTitle("2014春夏");
blog2.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog2.setContext(context);
Blog blog3 = new Blog();
blog3.setTime("2014-04-25 12:35:00");
blog3.setAuthor("1344360230");
blog3.setTitle("201");
blog3.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog3.setContext(context);
Blog blog4 = new Blog();
blog4.setTime("2014-04-25 12:35:00");
blog4.setAuthor("1344360230");
blog4.setTitle("2014春夏色彩妆出来");
blog4.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog4.setContext(context);

HashSet<Blog> set = new HashSet<Blog>();
set.add(blog1);
set.add(blog2);
set.add(blog3);
set.add(blog4);
System.out.println(set.contains(blog4));

}

}


问题:
不知道是什么原因,一般的blog比较(见测试方法)运行理想,但用在我的爬虫里就会出现 如下情况
控制台打印:
正在初始化blog数据缓存...
blog数据缓存初始化完毕
HashSet集合内blog个数:1967
2014-04-28 22:09:41---------2014-04-28 18:27:49
长腿叔叔---------文怡
【香港】寻找米其林,一场为了舌尖的旅行--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_55f01d310102egnb.html?tj=1成功,已成功录入1条
2014-04-28 08:54:56---------2014-04-28 18:27:49
马未都---------文怡
第1093篇?散文--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_5054769e0102effa.html?tj=1#commonComment成功,已成功录入2条
2014-04-29 08:39:53---------2014-04-28 18:27:49
快乐宝贝0399---------文怡
写在孩子十一岁生日--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_a0ba06a00101innl.html?tj=1成功,已成功录入3条
2014-04-28 22:21:55---------2014-04-28 18:27:49
大佬鸣---------文怡
2014年4月恒大里水基地开放日盛况分享(多图)--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_51ef9d460102f427.html?tj=1#commonComment成功,已成功录入4条
2014-04-28 06:57:53---------2014-04-28 18:27:49
郑渊洁---------文怡
我在纽约街头用国歌吓唬在逃贪官--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_473abae60102dzy9.html?tj=1#commonComment成功,已成功录入5条
入库http://blog.sina.com.cn/s/blog_4eddf60c0102e2jh.html?tj=1#commonComment成功,已成功录入6条
2014-04-28 09:26:56---------2014-04-28 18:27:49
董士鸿周易预测---------文怡
如何根据手相看事业运--------- 完全不会做饭的人,用新玩具试试这道菜-----油焖番茄虾
入库http://blog.sina.com.cn/s/blog_5f2e43a30102egx9.html?tj=1#commonComment成功,已成功录入7条
2014-04-26 20:30:10---------2014-04-28 18:27:49
【解惑】关于HashSet中contains和add方法的疑问——实体类中equals和hashCode方法的重写
为什么会出现这种情况?是我hashCode重写的有问题吗?
还是源码逻辑结构有问题?

6 个解决方案

#1


不要沉么~    求助SOS

#2


再顶一个~   我打印的日志是为了看看在比较的过程中都是哪些blog进行了比较。
很疑惑 为什么我新爬的blog总是和 同一个blog比较?

#3


我把代码摘出来 写了个测试类, 出现了同样的问题

package com.test.httpclient.page;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;

import com.test.util.LinkToMySQL;

/**
 * Blog实体类
 * @author Administrator
 *
 */
public class Blog {

private String time;
private String author;
private String title;
private String context;
private String url;

public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}

//通过对比时间、作者、标题来判断blog是否相同
@Override
public boolean equals(Object o) {
if(o == null){
return false;
}
if(o == this){
return true;
}
if(o.getClass() == Blog.class){
Blog blog = (Blog)o;
//这里,检查一下每次抓取的blog 和 从数据库中 取出的  有什么不同。
System.out.println(time + "---------" + blog.time);
System.out.println(author + "---------" + blog.author);
System.out.println(title + "---------" + blog.title);
System.out.println(this.hashCode() + "--------" + blog.hashCode());
return time.equals(blog.time) &&
author.equals(blog.author) &&
title.equals(blog.title)&&
this.hashCode() == blog.hashCode()
;
}
return false;
}

//根据时间+作者+标题计算Blog对象的hashCode()返回值
@Override
public int hashCode() {
return (time+author+title).hashCode();
}

public static void main(String[] args){
String context = "2014年的春夏是一个非常多元的春夏,你会看到有光的运用、水彩色的使用还有自然水润的裸妆等,今天呢我会为大家介绍几款流行的彩妆。今年非常流行有个性的眼妆,你可以看起起来像吸血鬼,看起来像猫,还可以是几何线条,这是我特意为龄之打造了一款有一点点变形款的几何猫眼,看起来是不是超有时尚感!除了个性的眼妆今年还很流行糖果色,大家都知道泡泡眼画糖果色眼影通常显得泡泡的,今天我就要教大家如何来画水彩妆~首先利用一些基本的咖啡色眼影放在你的眼球上缘做打底画出眼部轮廓,不用靠近你的睫毛; 接下来,再选择你想要的糖果色眼线笔,用眼影刷蘸取刷在睫毛根部,这样的好处是线条不会生硬,再用糖果色眼影点在睫毛根部并向上渐层晕染,这个时候如果感觉还是需要一点点眼线的话,我们的眼线只要加尾端就好了;在画完这样的水彩眼影后,记得要刷出根根分明的睫毛哦!其实无论彩妆如何变化,可是最基本的一点就是需要拥有一个无暇静透的底妆,今年的底妆就是要打造裸肌的感觉,但这不是每个女孩都能拥有先天比较好的肌肤状态,尤其是在春天这样干燥、易敏感的季节,很多的美眉都会有痘痘的困扰,爱美是女生的天性,不可能就素颜顶着痘痘出门,那要怎么办?哈哈今天我要给大家介绍祛痘神器,那就是我的宝贝净肌G16无瑕净痘凝露,也就是我们俗称的痘痘急救棒!使用起来非常方便,在清洁肌肤后,我们用急救棒均匀地涂抹在痘痘上,它会帮你对痘痘进行保护,再上妆的话,化妆品和痘痘不会直接地接触,就不会受到刺激。还可以帮助痘痘快速地消炎、杀菌、并且还可以加速毛囊中的油脂代谢排出,防止痘痘的感染和复发。痘痘急救棒还有另一个功效,那就是缩短痘痘生长周期,一般正常的痘痘,从出现到平复红肿,生长周期为一周。实验室测试证实,净肌痘痘急救棒可将这个周期大大缩短到1-3天!而且这款急救棒里面添加舒敏配方,即便是油性敏感肌也可放心使用哦~~~";
Blog blog1 = new Blog();
blog1.setTime("2014-04-25 12:35:00");
blog1.setAuthor("1344360230");
blog1.setTitle("2014春夏色彩妆出来");
blog1.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog1.setContext(context);
// Blog blog2 = new Blog();
// blog2.setTime("2014-04-25 12:35:00");
// blog2.setAuthor("1344360230");
// blog2.setTitle("2014春夏色彩妆出");
// blog2.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog2.setContext(context);
// Blog blog3 = new Blog();
// blog3.setTime("2014-04-25 12:35:00");
// blog3.setAuthor("1344360230");
// blog3.setTitle("2014春夏色彩妆");
// blog3.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog3.setContext(context);
// Blog blog4 = new Blog();
// blog4.setTime("2014-04-25 12:35:00");
// blog4.setAuthor("1344360230");
// blog4.setTitle("2014春夏色彩妆出来");
// blog4.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog4.setContext(context);
//
// HashSet<Blog> set = new HashSet<Blog>();
// set.add(blog1);
// set.add(blog2);
// set.add(blog3);
// set.add(blog4);
// System.out.println(set.contains(blog4));

try {
Connection conn = LinkToMySQL.getConnection();
ResultSet rs = conn.createStatement().executeQuery("select * from main");
Blog blog = new Blog();
System.out.println("正在初始化blog数据缓存...");
HashSet<Blog> tempBlogSet = new HashSet<Blog>(2048);
while(rs.next()){
blog.setTime(new String(rs.getString("time")));
blog.setAuthor(new String(rs.getString("author")));
blog.setTitle(new String(rs.getString("title")));
tempBlogSet.add(blog);
}
System.out.println("blog数据缓存初始化完毕");
System.out.println("HashSet集合内blog个数:" + tempBlogSet.size());
//释放资源
if(rs != null){
rs.close();
}
System.out.println(tempBlogSet.contains(blog1));
System.out.println("添加之前缓存大小:"+tempBlogSet.size());
tempBlogSet.add(blog1);
System.out.println("添加之后:"+tempBlogSet.size());
} catch (SQLException e) {
e.printStackTrace();
}


}

}



日志:
正在初始化blog数据缓存...
blog数据缓存初始化完毕
HashSet集合内blog个数:1968
2014-04-25 12:35:00---------2014-04-29 09:57:37
1344360230---------金汕博客
2014春夏色彩妆出来---------从华 国 锋接受中纪委质询看当时的党风
1661038444---------2097166136
false
添加之前缓存大小:1968
2014-04-25 12:35:00---------2014-04-29 09:57:37
1344360230---------金汕博客
2014春夏色彩妆出来---------从华 国 锋接受中纪委质询看当时的党风
1661038444---------2097166136
添加之后:1969

貌似被测blog只和一个莫名其妙的blog比了一次~~

#4


你contains之后不put进去那个Set里的吗?

如果没遇上rehash的状况
A与B对比插入后,C再对比应该会与之前的A和B对比,而不只是和A对比




#5


引用 4 楼 kiyoki 的回复:
你contains之后不put进去那个Set里的吗?

如果没遇上rehash的状况
A与B对比插入后,C再对比应该会与之前的A和B对比,而不只是和A对比

我不太明白为什么会出现这样的对比方式~  是hash值的原因吗?

#6


问题在百度java吧中得到解决 下面是链接。
希望同样犯错的同学以此为鉴。
http://tieba.baidu.com/p/3012488694

#1


不要沉么~    求助SOS

#2


再顶一个~   我打印的日志是为了看看在比较的过程中都是哪些blog进行了比较。
很疑惑 为什么我新爬的blog总是和 同一个blog比较?

#3


我把代码摘出来 写了个测试类, 出现了同样的问题

package com.test.httpclient.page;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;

import com.test.util.LinkToMySQL;

/**
 * Blog实体类
 * @author Administrator
 *
 */
public class Blog {

private String time;
private String author;
private String title;
private String context;
private String url;

public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}

//通过对比时间、作者、标题来判断blog是否相同
@Override
public boolean equals(Object o) {
if(o == null){
return false;
}
if(o == this){
return true;
}
if(o.getClass() == Blog.class){
Blog blog = (Blog)o;
//这里,检查一下每次抓取的blog 和 从数据库中 取出的  有什么不同。
System.out.println(time + "---------" + blog.time);
System.out.println(author + "---------" + blog.author);
System.out.println(title + "---------" + blog.title);
System.out.println(this.hashCode() + "--------" + blog.hashCode());
return time.equals(blog.time) &&
author.equals(blog.author) &&
title.equals(blog.title)&&
this.hashCode() == blog.hashCode()
;
}
return false;
}

//根据时间+作者+标题计算Blog对象的hashCode()返回值
@Override
public int hashCode() {
return (time+author+title).hashCode();
}

public static void main(String[] args){
String context = "2014年的春夏是一个非常多元的春夏,你会看到有光的运用、水彩色的使用还有自然水润的裸妆等,今天呢我会为大家介绍几款流行的彩妆。今年非常流行有个性的眼妆,你可以看起起来像吸血鬼,看起来像猫,还可以是几何线条,这是我特意为龄之打造了一款有一点点变形款的几何猫眼,看起来是不是超有时尚感!除了个性的眼妆今年还很流行糖果色,大家都知道泡泡眼画糖果色眼影通常显得泡泡的,今天我就要教大家如何来画水彩妆~首先利用一些基本的咖啡色眼影放在你的眼球上缘做打底画出眼部轮廓,不用靠近你的睫毛; 接下来,再选择你想要的糖果色眼线笔,用眼影刷蘸取刷在睫毛根部,这样的好处是线条不会生硬,再用糖果色眼影点在睫毛根部并向上渐层晕染,这个时候如果感觉还是需要一点点眼线的话,我们的眼线只要加尾端就好了;在画完这样的水彩眼影后,记得要刷出根根分明的睫毛哦!其实无论彩妆如何变化,可是最基本的一点就是需要拥有一个无暇静透的底妆,今年的底妆就是要打造裸肌的感觉,但这不是每个女孩都能拥有先天比较好的肌肤状态,尤其是在春天这样干燥、易敏感的季节,很多的美眉都会有痘痘的困扰,爱美是女生的天性,不可能就素颜顶着痘痘出门,那要怎么办?哈哈今天我要给大家介绍祛痘神器,那就是我的宝贝净肌G16无瑕净痘凝露,也就是我们俗称的痘痘急救棒!使用起来非常方便,在清洁肌肤后,我们用急救棒均匀地涂抹在痘痘上,它会帮你对痘痘进行保护,再上妆的话,化妆品和痘痘不会直接地接触,就不会受到刺激。还可以帮助痘痘快速地消炎、杀菌、并且还可以加速毛囊中的油脂代谢排出,防止痘痘的感染和复发。痘痘急救棒还有另一个功效,那就是缩短痘痘生长周期,一般正常的痘痘,从出现到平复红肿,生长周期为一周。实验室测试证实,净肌痘痘急救棒可将这个周期大大缩短到1-3天!而且这款急救棒里面添加舒敏配方,即便是油性敏感肌也可放心使用哦~~~";
Blog blog1 = new Blog();
blog1.setTime("2014-04-25 12:35:00");
blog1.setAuthor("1344360230");
blog1.setTitle("2014春夏色彩妆出来");
blog1.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
blog1.setContext(context);
// Blog blog2 = new Blog();
// blog2.setTime("2014-04-25 12:35:00");
// blog2.setAuthor("1344360230");
// blog2.setTitle("2014春夏色彩妆出");
// blog2.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog2.setContext(context);
// Blog blog3 = new Blog();
// blog3.setTime("2014-04-25 12:35:00");
// blog3.setAuthor("1344360230");
// blog3.setTitle("2014春夏色彩妆");
// blog3.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog3.setContext(context);
// Blog blog4 = new Blog();
// blog4.setTime("2014-04-25 12:35:00");
// blog4.setAuthor("1344360230");
// blog4.setTitle("2014春夏色彩妆出来");
// blog4.setUrl("http://blog.sina.com.cn/s/blog_50214f260102gzs9.html?tj=1#commonComment");
// blog4.setContext(context);
//
// HashSet<Blog> set = new HashSet<Blog>();
// set.add(blog1);
// set.add(blog2);
// set.add(blog3);
// set.add(blog4);
// System.out.println(set.contains(blog4));

try {
Connection conn = LinkToMySQL.getConnection();
ResultSet rs = conn.createStatement().executeQuery("select * from main");
Blog blog = new Blog();
System.out.println("正在初始化blog数据缓存...");
HashSet<Blog> tempBlogSet = new HashSet<Blog>(2048);
while(rs.next()){
blog.setTime(new String(rs.getString("time")));
blog.setAuthor(new String(rs.getString("author")));
blog.setTitle(new String(rs.getString("title")));
tempBlogSet.add(blog);
}
System.out.println("blog数据缓存初始化完毕");
System.out.println("HashSet集合内blog个数:" + tempBlogSet.size());
//释放资源
if(rs != null){
rs.close();
}
System.out.println(tempBlogSet.contains(blog1));
System.out.println("添加之前缓存大小:"+tempBlogSet.size());
tempBlogSet.add(blog1);
System.out.println("添加之后:"+tempBlogSet.size());
} catch (SQLException e) {
e.printStackTrace();
}


}

}



日志:
正在初始化blog数据缓存...
blog数据缓存初始化完毕
HashSet集合内blog个数:1968
2014-04-25 12:35:00---------2014-04-29 09:57:37
1344360230---------金汕博客
2014春夏色彩妆出来---------从华 国 锋接受中纪委质询看当时的党风
1661038444---------2097166136
false
添加之前缓存大小:1968
2014-04-25 12:35:00---------2014-04-29 09:57:37
1344360230---------金汕博客
2014春夏色彩妆出来---------从华 国 锋接受中纪委质询看当时的党风
1661038444---------2097166136
添加之后:1969

貌似被测blog只和一个莫名其妙的blog比了一次~~

#4


你contains之后不put进去那个Set里的吗?

如果没遇上rehash的状况
A与B对比插入后,C再对比应该会与之前的A和B对比,而不只是和A对比




#5


引用 4 楼 kiyoki 的回复:
你contains之后不put进去那个Set里的吗?

如果没遇上rehash的状况
A与B对比插入后,C再对比应该会与之前的A和B对比,而不只是和A对比

我不太明白为什么会出现这样的对比方式~  是hash值的原因吗?

#6


问题在百度java吧中得到解决 下面是链接。
希望同样犯错的同学以此为鉴。
http://tieba.baidu.com/p/3012488694