我的输入数据是一个列表中的50,000(或更多)电子邮件消息,并且dur中的多个收件人在to,cc和bcc中存在大量重复。因此,我需要从此列表中提取唯一的消息。
我必须比较Message的某些属性(From,To list和contains(String only))以确定是否存在相同的内容。
现在我将这50,000条消息分成50个小1000的消息列表,并在它的主题中运行每个小列表的重复。
所有线程将它的输出添加到一个列表中,最后我检查该线程中的重复。 当我这样做时,我的JVM达到1.25 GB内存。
因此,如果我尝试推送超过50,000条消息,我会收到内存不足错误。
我有一个名为removeDeduplicate(array of messages, blank list)
的方法,它将消息数组和空列表作为输入,并在该空白列表中返回唯一消息。
这是我的代码:
public Message[] processForDeduplication(Message[] msgs) throws MessagingException, IOException, InterruptedException {
final List<Message> output = new ArrayList<Message>();
if(msgs.length < MAX_MSG){
output.addAll(removeDeduplication(msgs, new ArrayList<Message>()));
} else {
List<Thread> threads = new ArrayList<Thread>();
int index = 0, lastIndex = MAX_MSG;
while(index < msgs.length){
if(lastIndex >= msgs.length) {
lastIndex = msgs.length;
}
final Message[] temp = Arrays.copyOfRange(msgs, index, lastIndex);
Thread t = new Thread(new Runnable(){
@Override
public void run() {
try {
output.addAll(removeDeduplication(temp, new ArrayList<Message>()));
} catch (MessagingException ex) {
logger.error(EmailComparator.class.getName() + ex);
} catch (IOException ex) {
logger.error(EmailComparator.class.getName() + ex);
}
}
});
t.start();
threads.add(t);
index = lastIndex;
lastIndex = lastIndex + MAX_MSG;
}
for(Thread t: threads){
while(t.isAlive()){
Thread.sleep(100);
}
}
threads = null;
}
List<Message> results = removeDeduplication(convertToArray(output), new ArrayList<Message>());
return convertToArray(results);
}
我正在尝试微调我的代码以获得内存增强和性能。 现在需要大约12-15秒来完成50,000条记录,我希望这是5-6秒。
答案 0 :(得分:1)
我不确定你的Message
是什么,所以我认为它是javax.mail.Message
。我创建了一个包装器对象,用于检查指定的消息是否相等。此对象将from
和to
数组缓存为Set
s - 这允许更快的等于比较,因为Set
具有O(1)包含方法。
包装器还会缓存hashCode
,以便Set
不必重新计算它。
public static class MessageWrapper {
private final Message message;
private final Set<Address> from;
private final Set<Address> to;
private final Object content;
private final int hashCode;
public MessageWrapper(Message message) throws MessagingException, IOException {
this.message = message;
this.from = new HashSet<Address>(Arrays.asList(message.getFrom()));
this.to = new HashSet<Address>(Arrays.asList(message.getRecipients(Message.RecipientType.TO)));
this.content = message.getContent();
this.hashCode = calcHashCode();
}
public Message getMessage() {
return message;
}
private int calcHashCode() {
int hash = 7;
hash = 37 * hash + (this.from != null ? this.from.hashCode() : 0);
hash = 37 * hash + (this.to != null ? this.to.hashCode() : 0);
hash = 37 * hash + (this.content != null ? this.content.hashCode() : 0);
return hash;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MessageWrapper other = (MessageWrapper) obj;
if (this.from != other.from && (this.from == null || !this.from.equals(other.from))) {
return false;
}
if (this.to != other.to && (this.to == null || !this.to.equals(other.to))) {
return false;
}
if (this.content != other.content && (this.content == null || !this.content.equals(other.content))) {
return false;
}
return true;
}
}
存储最昂贵的东西实际上是content
- 您可能只想考虑存储content.hashCode
然后进行比较;但这会让冲突发生。
现在您需要做的就是将所有Message
放入MessageWrapper
并将其放入HashSet
- 这会自动删除equals()
<的项目/ p>
public Message[] processForDeduplication(final Message[] messages) throws MessagingException, IOException {
final Set<MessageWrapper> messageWrappers = new HashSet<MessageWrapper>(messages.length, 1.0f);
for (final Message m : messages) {
messageWrappers.add(new MessageWrapper(m));
}
final List<Message> ms = new ArrayList<Message>(messages.length);
for (final MessageWrapper wrapper : messageWrappers) {
ms.add(wrapper.getMessage());
}
return ms.toArray(new Message[messages.length]);
}
这有点乱,因为你必须在最后将事物转换回Message[]
。
显然,如果您的Message
不是javax.mail.Message
,则实施可能会有所不同。您甚至可以直接在相关课程上实施equals
和hashCode
。
答案 1 :(得分:0)
@Perception&amp; @大卫 我试过你们的建议,代码如下所示。 现在情况变得更糟,只有20,000我得到OutOfMemory例外。
public Message[] processForDeduplicationNew(Message[] msgs) throws MessagingException, IOException, InterruptedException {
TreeSet<Message> output = new TreeSet<Message>(new Comparator<Message>(){
@Override
public int compare(Message msg1, Message msg2) {
try {
List<String> newRecipients = getRecipients(msg1.getAllRecipients());
List<String> recipients = getRecipients(msg2.getAllRecipients());
if (newRecipients.size() == recipients.size()) {
for (int i = 0; i < recipients.size(); i++) {
if (!StringUtils.equalsIgnoreCase(newRecipients.get(i), recipients.get(i))) {
return 1;
}
}
} else {
return 1;
}
if (!StringUtils.equalsIgnoreCase(((InternetAddress) msg1.getFrom()[0]).getAddress(), ((InternetAddress) msg2.getFrom()[0]).getAddress())) {
return 1;
}
BodyPart newMsgBody= ((Multipart) msg1.getContent()).getBodyPart(0);
BodyPart msgBody = ((Multipart) msg2.getContent()).getBodyPart(0);
if (!StringUtils.equalsIgnoreCase((String) newMsgBody.getContent(), (String) msgBody.getContent())) {
return 1;
}
} catch (MessagingException ex) {
java.util.logging.Logger.getLogger(EmailComparator.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(EmailComparator.class.getName()).log(Level.SEVERE, null, ex);
}
return 0;
}
});
for (Message message : msgs) {
output.add(message);
}
return output.toArray(new Message[0]);
}
更多想法...... 感谢