0

Java は参照によって変数を渡しません。その場合、データ構造のようなListIteratorものは、対応するリストにどのように変更を加えるのでしょうか?

これが私が書いているイテレータの例です:

public class OdpIterator<E> implements ListIterator<E> {

    private OdpList<E> list;
    private int cursor;

    public OdpIterator(OdpList<E> list) {
        this.list = list;
    }

    @Override
    public void add(E arg0) {
        list.add(arg0);
    }

しかし、 を変更しようとしても、基になるリストは変更listadd()れないため、次のテストは失敗します。

OdpList<Integer> list = new OdpList<Integer>();
ListIterator<Integer> iter = list.listIterator();
iter.add(42);
assertTrue(list.contains(42));

OdpList 追加: 単体テストに合格しているので、正しいと思います。

@Override
public boolean add(E arg0) {
    ListCell<E> cell = new ListCell<E>(arg0);

    if (size() > 0) { //if something is already in the list
        tail.setNext(cell);
        tail = cell;
    }
    else {
        head = cell;
        tail = cell;
    }
    return true;
}

ListCell コンストラクター:

public class ListCell<T> {
    public ListCell(T arg0) {
        this.datum = arg0;
        next = null;
    }
}

OdpList listIterator:

@Override
public ListIterator<E> listIterator() {
    return new OdpIterator<E>(this);
}

OdpList には以下が含まれます。

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) == -1;
}

@Override
public int indexOf(Object arg0) {
    return findAfter(head, arg0, 0);
}

private int findAfter(ListCell<E> o, Object search, int soFar) {
    if (o == null) {
        return -1;
    }
    if (o.getDatum() == null && search != null) {
        return findAfter(o.getNext(), search, soFar + 1);           
    }
    if ((o.getDatum() == null && search == null) || o.getDatum().equals(search)) {
        return soFar;
    }

    return findAfter(o.getNext(), search, soFar + 1);
}

どうすればいいですか?それとも、イテレータの仕組みを誤解していますか?

4

7 に答える 7

6

人々が経験してきたすべての精神的な練習の後で、私はほとんどこれを言うのが嫌いですが... 問題は単にタイプミスです.

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) == -1;
}

する必要があります

@Override
public boolean contains(Object arg0) {
    return indexOf(arg0) != -1;
}

containstrueオブジェクトがリストにない場合にのみ返されました!

于 2009-10-15T17:45:12.087 に答える
2

それが機能する理由は、Java が参照型を渡すためです。それらは値によって渡され、これは多くの人にとって混乱の原因です。私の意見では、この混乱は、Java がオブジェクトを参照で渡し、プリミティブを値で渡すと人々が言い始めるとさらに大きくなります。両方をサポートする言語を使用する場合はなおさらです。

そのため、以下では少し話を逸らし、それがどのように機能するかを説明します。


Java は参照型を値で渡します。Java Ranch には、これを説明する 2 つの優れた記事があります。

  1. カップサイズ -- 変数についての話
  2. Pass-by-Value Please (カップサイズの続き)

こちらにもアスキーアートを使って投稿しています。もう一度やってみましょう。

メソッドがあります:

void test(StringBuilder fred) {
    fred.append("Fred");
}

そして、次のコード:

StringBuilder b = new StringBuilder();
test(b);

StringBuilder b = new StringBuilder();

メモリでは、これは次のようになります。

b -- > ""

test(b);

これにより、新しい変数 "b" が作成されますが、この変数は同じ文字列バッファーを指しています。

メモリでは、これは次のようになります。

b -- +
     +-> ""
fred-+

fred.append("Fred");

「fred」と「b」は異なる変数ですが、同じものを指しています。したがって、「fred」を変更すると、「b」も変更されます。

メモリでは、これは次のようになります。

b -- +
     +-> "Fred"
fred-+

 }

今度は "fred" が範囲外になり、食べられます。

b -- > "Fred"

これは、PBR b と fred が 1 になるという点で「参照渡し」とは異なります。上記の例では、次のような場所を除いて、ほとんど違いはありません。

b -- +
     +-> "Fred"
fred-+

PBR では次のようになります: b, fred --> "Fred"

「fred」が指している場所を変更しようとすると、PBR が実際に現れます。メソッドを次のように変更すると:

void test(StringBuilder fred) {
    fred = new StringBuilder("Fred");
}

違いがわかります。


StringBuilder b = new StringBuilder();

メモリでは、これは次のようになります。

b -- > ""

test(b);

参照型を値で渡す場合、次のようになります。

b -- +
     +-> ""
fred-+

しかし、PBR の場合は次のようになります。

b, fred--> ""

    fred = new StringBuilder("Fred");

ここで違いを見ていきます。値による参照の受け渡し (Java がサポートするもの) では、次のようになります。

b --> ""

fred--> "Fred"

それらの間のリンクをどのように壊したかを見てください。ただし、PBR ではリンクを保持します。

           "" // Old string builder now float in memory all lost and alone.
b, fred--> "Fred"
于 2009-10-15T16:58:11.230 に答える
0

Java はすべてのオブジェクトを参照渡しします。プリミティブを値で渡します。

コードで基になるリストを変更する必要があります。OdpList クラスに問題がないか確認します。

于 2009-10-15T16:32:55.667 に答える
0

一部のコード ( の定義) が欠落しているため、OdpListここで何が起こっているのかを説明するのは困難です。表示されているコードは正しいようです。リストの実装では、次のようになると思います。

public ListIterator<T> listIterator() {
  return new OdpIterator<T>(this);
}

Java は参照を渡しませんが、参照を値で渡します。そのため、参照が利用可能である限り、リストを変更しても問題はありません。

于 2009-10-15T16:37:28.900 に答える
0

なぜ(no code of seen)をlist.listIterator()返す必要があるのですか? そして、「Java は参照によって変数を渡さない」と誰が言いますか? すべてのインスタンス パラメータは値渡しされますが、オブジェクトへの参照は、値渡しのためにコピーされたポインタです。このコピーを参照すると、常に元のオブジェクトが変更されます!OdpIteratorOdpList

于 2009-10-15T16:37:29.280 に答える
0

ここでの問題はイテレータ自体ではなく、基礎となるジェネリックと関連する型の消去にあると思います。これに加えてオートボクシングを行うと、痛みのレシピが得られます。テストコードを見てください:

OdpList<Integer> list = new OdpList<Integer>();
ListIterator<Integer> iter = list.listIterator();
iter.add(42);
assertTrue(list.contains(42));

次に、このページここを見て、実際に何が起こっているのかを理解してください。ジェネリック型はコンパイラによって使用され、ランタイム環境によって無視されます。

Integer 型のリストをインスタンス化していますが、実行時に JVM がリスト反復子の内容を正確に把握することは不可能です。明示的なインスタンス化が行われなかったようで、一般的なオブジェクトに固執しています。これは、42 の自動魔法のオートボクシングが発生しないことを意味します。これは、その動作が Integer クラスに関連付けられているためです。実際、JVM はおそらく "list.contains(42)" の 42 を Object 型のオブジェクトへの参照として扱っているため、テストが失敗する理由が説明されています。

直感に反する?はい。ずさんな?うん。イライラしますか?とてつもなく。このような例が、Java ではジェネリックが壊れていると多くの人が言う理由です。

于 2009-10-15T17:23:36.670 に答える
0

まず第一に、プリミティブint型を値渡ししていません。autoboxed Integerインプレースで作成されたオブジェクトを参照によって渡しています。オートボクシングを避けるべき多くの正当な理由があります。

完全なOdpListリストを見ないとわかりにくいですが、 を調べるとOdpList.add、内部的に のリストでListCellsはなく のリストがあるように見えますIntegers。ある意味では、あなたはリンゴのかごの中のオレンジを探しているのです。

于 2009-10-15T17:24:15.737 に答える