0

配列リストを反復処理するときに配列リストを変更しようとすると、ConcurrentModificationException エラーが発生するのは事実です。しかし、最初に変更する要素を削除し、リストを繰り返し処理してから要素を挿入し直した場合、どのようにしてエラーが引き続き発生するのでしょうか? ロジックがこのようなものである場合、concurrentmodificationexception をスローする理由がわかりません。

このプログラム全体が Observer パターンを使用しています。


このプログラムの目的は、複数のテキスト ボックスを用意することです。1 つ入力すると、他のテキスト ボックスは同じテキストで更新されます。

件名- インターフェイス

public interface Subject {

public void attach(Observer o);
public void detach(Observer o);
public void notifyAllObservers();
}

SubjectImpl - Subject を実装します

import java.util.*;

public class SubjectImpl implements Subject {

private List <Observer> observers;

public SubjectImpl(){
    observers = new ArrayList<Observer>();
}


@Override
public void attach(Observer o) {
    observers.add(o);
}

@Override
public void detach(Observer o) {
    observers.remove(o);
}

@Override
public void notifyAllObservers() {
            //Iterating code
    for(Observer o: observers){
        o.update();
    }
}
}

オブザーバー- インターフェース

public interface Observer {
public void update();
}

エディター- オブザーバーを実装します

import java.awt.*
import java.io.*;
import javax.swing.*;



public class Editor extends JFrame implements DocumentListener, Observer {

private FileContentSubject reference;

    private JScrollPane textAreaScrollPane;
    private JTextArea textArea;


public Editor(FileContentSubject filecontentsubject) throws IOException {
    super("Editor");
    initComponents();

    this.reference = filecontentsubject;
    textArea.getDocument().addDocumentListener(reference);
    textArea.getDocument().putProperty("ownerEditor", this); 
}


private void initComponents(){

    textArea = new JTextArea();       

    //set location, size, visible and defaultcloseoperation here        

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(textArea, BorderLayout.CENTER);
}



@Override
public void update() {
    textArea.setText(reference.getState()); //Call to update each text box
}


@Override
public void changedUpdate(DocumentEvent e) {
}


@Override
public void insertUpdate(DocumentEvent e) {
}


@Override
public void removeUpdate(DocumentEvent e) {
}
}

ファイル コンテンツ システム- オブザーバーの具体的なサブジェクトとして機能します (存在するのは 1 つだけです)。

import javax.swing.event.*;
import javax.swing.text.*;

public class FileContentSubject implements Subject, DocumentListener {

private String state; //Current, most recent copy of everything

public String getState() {
    return this.state;
}

private SubjectImpl reference;

@Override
public void attach(Observer o) {
    reference.attach(o);
}

@Override
public void detach(Observer o) {
    reference.detach(o);
}

@Override
public void notifyAllObservers() {
    reference.notifyAllObservers();
}

public FileContentSubject() {
    reference = new SubjectImpl();
}

@Override
public void changedUpdate(DocumentEvent arg0) {
}

@Override
public void insertUpdate(DocumentEvent arg0) {

    Document doc = (Document) arg0.getDocument();
    try {
        this.state = doc.getText(0, doc.getLength());
    } catch (BadLocationException e) {
        e.printStackTrace();
    }

    Editor e = (Editor) doc.getProperty("ownerEditor");
    reference.detach(e); 
    notifyAllObservers(); 
    reference.attach(e); 
}

@Override
public void removeUpdate(DocumentEvent arg0) {

   //same as insertUpdate(DocumentEvent arg0) ^^
}

}
4

2 に答える 2

3

ConcurrentModificationException3つ以上のエディターが使用されている場合にのみスローされると思いますよね? とにかく、何が起こっているのか説明してみましょう。

Editor「Editor1」、「Editor2」、「Editor3」の 3 つのインスタンスがあるとします。

これは、「Editor1」にテキストを入力したとき (または、「一連のイベントを開始したとき」と呼んでいます) に起こることです:

User types on Editor1 // chain begins
FileContentSubject detaches editor: Editor1
FileContentSubject begin notifyAllObservers(): [Editor2, Editor3]
    Editor2 receives update() to new state

ここまでは正常な動作ですよね?問題は、Editor2'supdate()が呼び出されると、Editor2's が変更され、それtextArea( Editor1 's のように)変更されることです。FileContentSubject DocumentListenertextAreatextArea

これにより新しいエディションがトリガーされ、新しい「一連のイベント」が作成されます。実際には、次のようになります (最初から繰り返します)。

User types on Editor1 // chain begins
FileContentSubject detaches editor: Editor1
FileContentSubject begin notifyAllObservers(): [Editor2, Editor3] // first notification
    Editor2 receives update() to new state // another chain begins! (the 1st chain didn't end yet!)
        FileContentSubject detaches editor: Editor2
        FileContentSubject begin notifyAllObservers(): [Editor3]
            Editor3 receives update() to new state // chain THREE!!
                FileContentSubject detaches editor: Editor3
                FileContentSubject begin notifyAllObservers(): [] // empty list... nobody to notify
                FileContentSubject reattaches editor: Editor3
                FileContentSubject ended notifyAllObservers(): [Editor3] // chain 3 over
        FileContentSubject reattaches editor: Editor2
        FileContentSubject ended notifyAllObservers(): [Editor3, Editor2] // chain 2 over

ConcurrentModificationException投げるポイントです。しかし、コードのどこにスローされるのでしょうか?

実際、それはオンSubjectImplです(!):

@Override
public void notifyAllObservers() {
    for (Observer o : observers) {
        o.update();
    }
}

何が問題ですか?

問題は次のとおりです。

  • // first notificationコメントのある行に呼び出しがあることに注意してくださいnotifyAllObservers()
  • 彼の時点で、接続されているオブザーバーのリストは (この順序で) [Editor2, Editor3] です。
  • notifyAllObservers()、呼び出されると、そのリストの反復を開始します
    • 反復の最初のステップは、 を呼び出すことEditor2.update()です。
      • Editor2.update()まったく新しいチェーンを開始します...
      • そのチェーンが終了すると、接続されたオブザーバーのリストは [Editor3, Editor2] になります (注意: 順序が変更されました!)
    • notifyAllObservers()のステップへの反復を続行しようとすると、リストが変更されたことに気付き、... バム! ConcurrentModificationException投げられます。

なんで?反復を開始したリストを変更することはできないためです。(イテレータを使用するか、「foreach」を使用します。)

ソリューション:

たくさんあるかもしれませんが、最も簡単なのは「一連のイベント」を止めることです。これを行うには、次のように変更update()Editorます。

@Override
public void update() {
    textArea.setText(reference.getState()); // Call to update each text box
}

に:

@Override
public void update() {
    textArea.getDocument().removeDocumentListener(reference);
    textArea.setText(reference.getState()); // Call to update each text box
    textArea.getDocument().addDocumentListener(reference);
}

どういう意味ですか?これは、がその に入力したのではなく、他の誰かによってEditor変更された場合、彼は( ) にもう通知せず、静かに変更するだけであることを意味します。textAreaFileContentSubjectDocumentListener

これにより、イベントの 2 番目のチェーンが最初から開始されなくなります。

問題を説明できたことを願っています!何かポイントを明確にすることができる場合は、お知らせください。乾杯!

于 2013-04-17T05:07:26.000 に答える