2016-04-15 13 views
12

これは最近プロジェクトで私を迷惑にしていました。私のGoogleのphooは適切な答えを見つけるのに失敗しています。ListIteratorを使用し、ユニークなリストであるコレクション

コレクションには、ListIteratorへのアクセス権がありますが、コレクション内の一意の値のみが許可されていますか?

これを推論すると、このコレクションには各要素の1つしか存在しないアイテムのコレクションがあります。私はまた、両方向でこのコレクションを横断できるようにしたい。しかし、これは遠い

public class UniqueArrayList<E> extends ArrayList<E> { 
    @Override 
    public boolean add(E element){ 
     if (this.contains(element)) 
      return false; 
     else 
      return super.add(element); 
    } 

    @Override 
    public void add(int index, E element){ 
     if (this.contains(element)) 
      return; 
     else 
      super.add(index, element); 
    } 

    @Override 
    public boolean addAll(Collection<? extends E> c){ 
     if (new HashSet<E>(c).size() < c.size()) 
      return false; 
     for(E element : c){ 
      if (this.contains(c)) 
       return false; 
     } 
     return super.addAll(c); 
    } 

    @Override 
    public boolean addAll(int index, Collection<? extends E> c) { 
     if (new HashSet<E>(c).size() < c.size()) 
      return false; 
     for(E element : c){ 
      if (this.contains(c)) 
       return false; 
     } 
     return super.addAll(index, c); 
    } 

    @Override 
    public ListIterator<E> listIterator(int index) { 
     if (index < 0 || index > this.size()) 
      throw new IndexOutOfBoundsException("Index: "+index); 
     return new ListItr(index); 
    } 

    @Override 
    public ListIterator<E> listIterator() { 
     return new ListItr(0); 
    } 

    @Override 
    public Iterator<E> iterator() { 
     return new Itr(); 
    } 

    private class Itr implements Iterator<E> { 
     int cursor;  // index of next element to return 
     int lastRet = -1; // index of last element returned; -1 if no such 
     int expectedModCount = modCount; 

     public boolean hasNext() { 
      return cursor != size(); 
     } 

     @SuppressWarnings("unchecked") 
     public E next() { 
      checkForComodification(); 
      int i = cursor; 
      if (i >= size()) 
       throw new NoSuchElementException(); 
      Object[] elementData = UniqueArrayList.this.toArray(); 
      if (i >= elementData.length) 
       throw new ConcurrentModificationException(); 
      cursor = i + 1; 
      return (E) elementData[lastRet = i]; 
     } 

     public void remove() { 
      if (lastRet < 0) 
       throw new IllegalStateException(); 
      checkForComodification(); 

      try { 
       UniqueArrayList.this.remove(lastRet); 
       cursor = lastRet; 
       lastRet = -1; 
       expectedModCount = modCount; 
      } catch (IndexOutOfBoundsException ex) { 
       throw new ConcurrentModificationException(); 
      } 
     } 

     final void checkForComodification() { 
      if (modCount != expectedModCount) 
       throw new ConcurrentModificationException(); 
     } 


    } 

    private class ListItr extends Itr implements ListIterator<E> { 
     ListItr(int index) { 
      super(); 
      cursor = index; 
     } 

     public boolean hasPrevious() { 
      return cursor != 0; 
     } 

     public int nextIndex() { 
      return cursor; 
     } 

     public int previousIndex() { 
      return cursor - 1; 
     } 

     @SuppressWarnings("unchecked") 
     public E previous() { 
      checkForComodification(); 
      int i = cursor - 1; 
      if (i < 0) 
       throw new NoSuchElementException(); 
      Object[] elementData = UniqueArrayList.this.toArray(); 
      if (i >= elementData.length) 
       throw new ConcurrentModificationException(); 
      cursor = i; 
      return (E) elementData[lastRet = i]; 
     } 

     public void set(E e) { 
      if (lastRet < 0) 
       throw new IllegalStateException(); 
      checkForComodification(); 
      try { 
       //Need to allow this for the collections sort to work! 
       //if (!UniqueArrayList.this.contains(e)) 
       UniqueArrayList.this.set(lastRet, e); 
      } catch (IndexOutOfBoundsException ex) { 
       throw new ConcurrentModificationException(); 
      } 
     } 

     public void add(E e) { 
      checkForComodification(); 

      try { 
       int i = cursor; 
       UniqueArrayList.this.add(i, e); 
       cursor = i + 1; 
       lastRet = -1; 
       expectedModCount = modCount; 
      } catch (IndexOutOfBoundsException ex) { 
       throw new ConcurrentModificationException(); 
      } 
     } 
    } 
} 

:と私は、ソートされたか、私は、私は、適切なものを発見し、次のコードを使用して独自のクラスを記述しなければならなかったいませんでしたCollections.Sort();

を使用して、それを並べ替えることができるようにすることのいずれかにそれをしたいです完璧から、Collections.sort();はリスト内の項目を移動するためにListIterator.set();を上書きすることはできません。一意でないアイテムがここにリストに追加されないようにしようとすると、ソートは決して起こりません。

誰かがより良い方法を持っているか、私が望む規則に従う別のコレクションを知っていますか?あるいは、ちょっと面倒な問題で生きていく必要がありますか?

[編集] これはCollections.sort();方法である:

public static <T extends Comparable<? super T>> void sort(List<T> list) { 
    Object[] a = list.toArray(); 
    Arrays.sort(a); 
    ListIterator<T> i = list.listIterator(); 
    for (int j=0; j<a.length; j++) { 
     i.next(); 
     i.set((T)a[j]); 
    } 
} 

彼らはこれを行うために与える推論は次のとおりです。

この実装は、配列に指定されたリストをダンプ 配列をソートします配列内の対応する位置の から各要素をリセットするリストを反復処理します。これにより、リンクされた リストをソートしようとしたときに発生するlog(n)パフォーマンスが、nのようになりません。

+0

は、Javaのバージョンを使用していますか? – Kayaman

+0

'Collections.sort();で' ListIterator.set(); 'が使われているという考えはどこで思いつきましたか? – Kayaman

+0

Java 1.7で、ソート方法 – Draken

答えて

5

前述のThor Stanと同じように、TreeSetはあなたの好きなものを最大限に活用します。要素が一意であることを保証し、並べ替えを維持し、iterator()またはdescendingIterator()を使用していずれかの方向に反復することができます。

ListIteratorを求めている理由は完全にはっきりしていません。 ListIteratorについてのほとんどのことは、非常にポジティブです。つまり、インデックスの概念、現在の位置に何かを追加すること、または現在の要素を設定することです。これらはソートされたセットには意味がありません。

あなたが探しているかもしれないListIteratorの1つの側面は、反復の途中で方向を逆転させる能力です。これはTreeSetイテレータで直接行うことはできません。ListIteratorの代わりに通常のIteratorでしかアクセスできないためです。

ただし、TreeSetは、NavigableSetインターフェイスを実装しています。このインターフェイスを使用すると、順番に要素を順番に切り替えることができます。 NavigableSetインターフェイスはSortedSetのサブインターフェイスで、first()およびlast()の方法を提供し、セットの「端」の1つで作業を開始できます。セット内に要素があれば、lower(E)higher(E)メソッドを使用していずれかの方向に進むことができます。または、途中で開始したい場合は、インデックスで開始することはできませんが、値のセット(そのメンバーでなくてもかまいません)から開始し、lower(E)またはhigher(E)を呼び出すことができます。例えば

TreeSet<String> set = new TreeSet<>(
     Arrays.asList("a", "z", "b", "y")); 
    String cur; 

    cur = set.first();  // a 
    cur = set.higher(cur); // b 
    cur = set.higher(cur); // y 
    cur = set.higher(cur); // z 
    cur = set.lower(cur); // y 
    cur = set.lower(cur); // b 
    cur = set.lower(cur); // a 
    cur = set.lower(cur); // null 
+0

これは実際に私が探していたものです。これらのメソッドは、私がやりたいことを行い、いずれの方向にも反復を許可します。私はこれを答えとして受け入れます。ソリューションをありがとう! – Draken

7

一意の値が必要な場合は、セットに切り替えるようにしてください。 TreeSetをComparatorインスタンスとともに使用すると、エントリをソートできます。 TreeSetのdescendingSet()メソッドは、逆順を返します。 本当にListIteratorが必要な場合は、そのセットから一時リストを作成できます。

+0

'descendingSet()'はその位置を知っていて、そのセットを通って前進することができますか、またはその位置を再配置し、前進する場合はそこから開始する必要がありますか? – Draken

+0

descendingSet()はSetを返します。したがって、位置はありません。 descendingIterator()は新しいIteratorインスタンスを返します。したがって、以前の呼び出しの位置は不明です。 –

+0

だから、TreeSetを実行するのが最善の策だと思っていますし、どちらの方向にもコレクションを移動する必要があるときは、一時的なリストを作成する必要があります。いくつかの中間的な理由がないということは少し残念です。私はもう数日は開いたままにしておきます。他の誰もより良い答えがない場合、これを答えとしてマークします – Draken

1

Javaでは、Listはユニークではないアイテムを許可し、Setは許可しないとみなします。セットは明らかにListIteratorsをサポートしていないため、ListIteratorを使用するコードでは、基になるコレクションはSetではないとみなすことができます。

Java 8では、ソートにはもうListIteratorが使用されていませんが、Java 7に慣れていれば、本当に役に立ちません。基本的には、あなたの文脈や使い方に大きく依存するので、Thor StanのようなSetを使用して、必要に応じてオンデマンドでListを作成すると便利です。

もう1つの方法は、重複をチェックしない方法でリストにアクセスする独自のListIteratorを提供することです。これは無関係のオブジェクトを作成しないという利点がありますが、Setオプションはコードが短くなり、パフォーマンスに大きな影響を及ぼす可能性はほとんどありません。

おそらく他のオプションもありますが、私は優雅なものは考えられません。

1

これは簡単な回答では問題になります。

add(int, E)の契約が壊れていることが問題です。このメソッドは、要素を追加するか、例外をスローする必要があります。要素を追加せずに戻ることはできません。

set(int, E)をオーバーライドすると、要素が設定されないことがあるため、その方法の契約が破綻し、特定した通りにCollections.sort()が機能しなくなります。

これらの契約を破棄することはお勧めできません。これにより、リストに作用する他の方法が予測できない動作をする可能性があります。

他にもこのような問題が発生しています。たとえば、SetUniqueListのjavaドキュメントを参照してください。

ArrayList.contains()は線形検索であるため、実装に伴うもう1つの問題は、非常に遅いことです。

一つの解決策は、ArrayListHashSetの両方を使用するクラスを書き、むしろ通常のバージョンの契約を壊すよりaddsetの独自のバージョンを、書くことだろう。これはテストされていませんが、あなたはそのアイディアを得ています。

public final class MyList<E extends Comparable<? super E>> extends AbstractList<E> { 

    private final List<E> list = new ArrayList<>(); 
    private final Set<E> set = new HashSet<>(); 

    public E get(int i) { 
     return list.get(i); 
    } 

    public int size() { 
     return list.size(); 
    } 

    public boolean tryAdd(E e) { 
     return set.add(e) && list.add(e); 
    } 

    public boolean tryAdd(int i, E e) { 
     if (set.add(e)) { 
      list.add(i, e); 
      return true; 
     } 
     return false; 
    } 

    public boolean trySet(int i, E e) { 
     return set.add(e) && set.remove(list.set(i, e)); 
    } 

    public boolean remove(Object o) { 
     return set.remove(o) && list.remove(o); 
    } 

    public void sort() { 
     Collections.sort(list); 
    } 

    // One bonus of this approach is that contains() is now O(1) 
    public boolean contains(Object o) { 
     return set.contains(o); 
    } 

    // rest omitted. 
} 

このようなものは、Listの契約を破ることはありません。このバージョンではaddsetAbstractListから継承された動作であるため、UnsupportedOperationExceptionを投げることに注意してください。あなたはmyList.sort()を呼び出すことによってsortにできるでしょう。また、listIteratorメソッドも機能しますが、setまたはaddには使用できません(ただし、removeは機能します)。

このListを反復処理しながら、あなたがaddset要素に必要な場合は、むしろListIteratorよりも、明示的なインデックスを使用する必要があります。個人的には、私はこれを大きな問題とは考えていません。 ListIteratorは、getという一定の時間を持たないLinkedListのようなリストの場合には必要ですが、ArrayListの場合は必須ですが、必須ではありません。

+1

それは単に他の場所で使用すべきではない内部クラスでした。契約を破るための悪い考え。私は 'TreeSet'に向かってさらに傾いていて、それを両方向で反復する必要がある場合は' ArrayList'に渡します。しかし、提案と助言に感謝します。私は別のクラスをオーバーライドしようとする次回の契約履行を見守っています! – Draken

関連する問題