0

我的输入数据是一个列表中的 50,000 条(或更多)电子邮件消息,并且在 to、cc 和 bcc 中的多个收件人之间存在大量重复。因此,我需要从此列表中提取唯一消息。

我必须比较 Message 的某些属性(发件人、收件人列表和包含(仅限字符串))以确定是否相同。

现在,我将这 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);
}

我也在尝试微调我的代码以提高内存和性能。现在完成 50,000 条记录大约需要 12-15 秒,我希望是 5-6 秒。

4

1 回答 1

1

我不确定你Message是什么所以我认为它是一个javax.mail.Message. 我创建了一个包装器对象,用于检查您指定的消息是否相等。该对象将fromto数组缓存为Sets - 这允许更快的等于比较,因为Sets 具有 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然后进行比较;然而,这将允许发生冲突。

现在您需要做的就是将所有Messages 放入MessageWrappers 并将其放入 a HashSet- 这将自动删除那些equals()

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[]最后将东西转换回 a 。

显然,如果您Message不是javax.mail.Message,实施可能会有所不同。您甚至可以直接在相关类上实现equals和实现。hashCode

于 2013-03-18T16:16:52.453 回答