لماذا يعد برنامج Java's Iterator غير قابل للتكرار؟

StackOverflow https://stackoverflow.com/questions/839178

  •  22-07-2019
  •  | 
  •  

سؤال

لماذا Iterator واجهة لا تمتد Iterable?

ال iterator() يمكن أن تعود الطريقة ببساطة this.

هل هذا عن قصد أم مجرد إشراف على مصممي Java؟

سيكون من المناسب أن تكون قادرًا على استخدام حلقة for-each مع التكرارات مثل هذا:

for(Object o : someContainer.listSomeObjects()) {
    ....
}

أين listSomeObjects() إرجاع مكرر.

هل كانت مفيدة؟

المحلول

ولأن مكرر يشير عموما إلى مثيل واحد في المجموعة. Iterable يعني أن واحدا قد الحصول على مكرر من كائن إلى اجتياز أكثر من عناصره - وليس هناك حاجة لتكرار أكثر من حالة واحدة، وهو ما يمثل مكرر

نصائح أخرى

ومكرر هو جليل. والفكرة هي أنه إذا كنت استدعاء Iterable.iterator() مرتين ستحصل <م> مستقلة المكررات - بالنسبة لمعظم iterables، على أي حال. من الواضح أنها لن تكون الحال في السيناريو الخاص بك.

وعلى سبيل المثال، أستطيع أن أكتب عادة:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

وهذا يجب طباعة جمع مرتين - ولكن مع المخطط الخاص الحلقة الثانية سوف تنتهي دائما على الفور

.

لبلدي 0.02 $، وأنا أتفق تماما أن مكرر لا ينبغي تنفيذ Iterable، ولكن أعتقد أن المعززة للحلقة أن تقبل أيضا. أعتقد أن كلها "جعل المكررات iterable" حجة تأتي كما عمل حول وجود عيب في اللغة.

وكان السبب كله لإدخال المعززة للحلقة أن "يلغي الكدح وخطأ التعرض للالمكررات والمتغيرات المؤشر عند بالتكرار عبر مجموعات والمصفوفات" [<لأ href = "http://download.oracle كوم / javase / 1.5.0 / مستندات / relnotes / features.html # forloop "يختلط =" noreferrer "عنوان =" [1] "> 1 ].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

لماذا إذن هذه الحجة نفسها لا عقد لالمكررات؟

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

في كلتا الحالتين، تمت إزالة المكالمات إلى hasNext () والقادم ()، وليس هناك أي إشارة إلى مكرر في حلقة داخلية. نعم، أنا أفهم أن Iterables يمكن إعادة استخدامها لخلق المكررات متعددة، ولكن كل ذلك يحدث خارج لحلقة: داخل الحلقة لا يوجد سوى من أي وقت مضى إلى الأمام عنصر التقدم في وقت واحد أكثر من البنود التي تم إرجاعها بواسطة مكرر <. / P>

وبالإضافة إلى ذلك، يسمح هذا من شأنه أيضا أن يجعل من السهل استخدام للحلقة لالتعدادات، والتي، كما أشير إلى ذلك في أي مكان آخر، هي مماثلة لالمكررات لا Iterables.

وحتى لا تجعل مكرر تنفيذ Iterable، ولكن تحديث للحلقة لقبول سواء.

وابتهاج،

وكما أشار من قبل الآخرين، Iterator و <أ href ل = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable هما شيئان مختلفان.

وأيضا، تطبيقات Iterator تسبق تعزيز للحلقات.

وكما أنها تافهة للتغلب على هذا القيد مع طريقة محول بسيط يشبه هذا عند استخدامها مع واردات أسلوب ثابت:

for (String line : in(lines)) {
  System.out.println(line);
}

وتنفيذ نموذج:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }

في جاوة 8 التكيف وIterator إلى Iterable يحصل على أبسط:

for (String s : (Iterable<String>) () -> iterator) {

وكما قال آخرون، وهو Iterable يمكن أن يطلق عليه عدة مرات، والعودة إلى مكرر جديدة على كل مكالمة. يتم استخدام مكرر مرة واحدة فقط. بحيث تكون ذات صلة، ولكن تخدم أغراضا مختلفة. محبط، ومع ذلك، فإن "اتفاق ل" الأسلوب يعمل فقط مع iterable.

وماذا سأذكر أدناه طريقة واحدة ليكون أفضل من كلا العالمين - العودة إلى Iterable (لتركيب أجمل) حتى عندما يكون تسلسل الكامن وراء البيانات لمرة واحدة

وهو خدعة لإرجاع تنفيذ مجهول من Iterable الذي يقوم بتشغيل فعلا العمل. وذلك بدلا من القيام بالعمل الذي يولد سلسلة لمرة واحدة ومن ثم العودة إلى مكرر على ذلك، يمكنك إرجاع Iterable التي، في كل مرة يتم الوصول إليه، redoes العمل. قد يبدو الإسراف، ولكن في كثير من الأحيان سوف ندعو فقط Iterable مرة واحدة على أي حال، وحتى لو كنت لا يطلق عليه عدة مرات، فإنه لا يزال لديه دلالات معقولة (على عكس مجمع بسيط هو أن يجعل مكرر "تبدو وكأنها" على Iterable، وهذا وون " ر تفشل إذا ما استخدمت مرتين).

وعلى سبيل المثال، ويقول لدي DAO التي توفر مجموعة من الكائنات من قاعدة بيانات، وأريد أن توفر الوصول إلى ذلك عن طريق مكرر (على سبيل المثال لتجنب خلق جميع الكائنات في الذاكرة إن لم يكن هناك حاجة إليها). الآن أنا فقط يمكن أن يعود مكرر، ولكن هذا يجعل استخدام القيمة التي تم إرجاعها في حلقة قبيحة. لذا بدلا من ذلك التفاف كل شيء في Iterable حالا:

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}

وهذه يمكن استخدامها بعد ذلك في التعليمات البرمجية مثل هذا:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

والذي يتيح لي استخدام المدمجة للحلقة في حين لا تزال تحتفظ استخدام الذاكرة تدريجيا.

وهذا النهج هو "كسول" - لا يتم العمل عند طلب Iterable، ولكن فقط في وقت لاحق عندما كرر محتويات أكثر - وعليك أن تكون على بينة من عواقب ذلك. في المثال مع DAO هذا يعني بالتكرار عبر النتائج داخل المعاملة قاعدة البيانات.

وهكذا هناك العديد من المحاذير، ولكن هذا يمكن أن يكون لا يزال لغة مفيدا في كثير من الحالات.

وبشكل لا يصدق، لم يقدم أحد آخر هذه الإجابة حتى الان. وإليك كيف يمكن "بسهولة" تكرار أكثر من Iterator باستخدام جافا 8 جديد <لأ href = "https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#forEachRemaining- java.util.function.Consumer- "يختلط =" noreferrer "> Iterator.forEachRemaining() الأسلوب:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

وبطبيعة الحال، هناك "بساطة" الحل الذي يعمل مع حلقة foreach مباشرة، يلف Iterator في امدا Iterable:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

وIterator هو واجهة الذي يسمح لك لتكرار على شيء. ذلك هو تنفيذ تتحرك من خلال مجموعة من نوع ما.

وIterable هو واجهة وظيفية الذي يدل على أن شيئا ما يحتوي على مكرر للوصول.

في Java8، وهذا يجعل الحياة سهلة جدا ... إذا كان لديك Iterator ولكن بحاجة إلى Iterable يمكنك القيام به ببساطة:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

وهذا يعمل أيضا في لحلقة:

for (T item : ()->someIterator){
    //doSomething with item
}

وأرى أيضا العديد من القيام بذلك:

public Iterator iterator() {
    return this;
}

ولكن هذا لا يجعل من حق! فإن هذا الأسلوب لا تكون ما تريد!

ومن المفترض أن iterator() طريقة لإرجاع مكرر جديدة تبدأ من الصفر. حتى واحد بحاجة إلى القيام بشيء من هذا القبيل:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(IterableIterator iter)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new MyClass(this.somedata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

والسؤال هو: لن يكون هناك أي طريقة لجعل فئة مجردة أداء هذا؟ بحيث للحصول على IterableIterator واحد فقط تحتاج إلى تنفيذ طريقتين المقبل () وhasNext ()

إذا أتيت إلى هنا بحثًا عن حل بديل، فيمكنك استخدامه IteratorIterable.(متوفر لـ Java 1.6 وما فوق)

مثال للاستخدام (عكس ناقل).

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

مطبوعات

one
two
two
one

أنا أتفق مع الإجابة المقبولة، ولكن أريد أن أضيف شرحي الخاص.

  • يمثل المكرر حالة الاجتياز، على سبيل المثال، يمكنك الحصول على العنصر الحالي من المكرر والانتقال إلى العنصر التالي.

  • يمثل Iterable مجموعة يمكن اجتيازها، ويمكنه إرجاع العدد الذي تريده من التكرارات، يمثل كل منها حالة الاجتياز الخاصة به، وقد يشير أحد المكررات إلى العنصر الأول، بينما قد يشير مكرر آخر إلى العنصر الثالث.

سيكون من الرائع أن تقبل Java for Loop كلاً من Iterator وIterable.

لمصلحة من البساطة، ومكرر وIterable مفهومان متميزة، Iterable هو مجرد اختصار ل "يمكنني إرجاع مكرر". أعتقد أن التعليمات البرمجية يجب أن تكون:

for(Object o : someContainer) {
}

ومع someContainer instanceof SomeContainer extends Iterable<Object>

وبوصفها جانبا: سكالا لديه طريقة toIterable () في مكرر. انظر سكالا التحويل الضمني أو الصريح من مكرر لiterable

وعلى صعيد ذات صلة، قد تجد محول IteratorIterable في أباتشي العموم Collections4 مفيدة. مجرد إنشاء مثيل من مكرر، وكان لديك iterable المقابل.

HTTPS: // commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

وID: org.apache.commons: المشاعات-collections4: 4.0

والمكررات وجليل، لديهم عنصر "المقبل" وأصبح "استنفدت" كرر مرة واحدة انتهت. لنرى أين المشكلة، تشغيل التعليمات البرمجية التالية، عدد الأرقام تطبع؟

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

ويمكنك محاولة المثال التالي:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top