0

私の入力データは、1 つのリストに 50,000 件 (またはそれ以上) の電子メール メッセージがあり、to、cc および bcc に複数の受信者が含まれているため、大量の重複があります。したがって、このリストから一意のメッセージを抽出する必要があります。

Message の特定のプロパティ (From、To リスト、contain(文字列のみ)) を比較して、同じかどうかを識別する必要があります。

現在、私はこの 50,000 件のメッセージを 50 個の小さな 1000 件のメッセージ リストに分割し、それぞれの小さなリストの複製をそのスレッドで実行しています。

すべてのスレッドがその出力を 1 つのリストに追加し、最後にそのスレッドの重複をチェックします。そうしているうちに、私の 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. 指定されたメッセージの等価性をチェックするラッパー オブジェクトを作成しました。fromこのオブジェクトはとto配列をs としてキャッシュします。これにより、 s には O(1) の contains メソッドがあるSetため、equals の比較が高速になります。ラッパーは、 によって再計算される必要がないように、 もキャッシュします。Set
hashCodeSet

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]);
}

最後に物事を a に戻す必要があるため、少し面倒ですMessage[]

明らかに、そうMessageでない場合javax.mail.Message、実装は異なる場合があります。equals問題のクラスにhashCode直接実装することさえできるかもしれません。

于 2013-03-18T16:16:52.453 に答える