Tekrarlanabilir Öğeleri Koleksiyona Dönüştürmenin Kolay Yolu


424

Benim uygulamada 3. parti kitaplığı (MongoDB için Bahar Verileri kesin) kullanın.

Iterable<T>Kodumu geri kalanı beklerken, bu kitaplığın yöntemleri döner Collection<T>.

Bir yerde hızlı bir şekilde diğerine dönüştürmeme izin verecek herhangi bir yardımcı yöntem var mı? foreachBöyle basit bir şey için kodumda bir sürü döngü oluşturmaktan kaçınmak istiyorum .


3
İşlemi gerçekleştirmek için kullanılan herhangi bir yöntem yine de koleksiyonun yinelemesine bağlıdır, bu nedenle herhangi bir performans kazancı bekleyemezsiniz. Ama sadece sözdizimsel şeker arıyorsanız Guava ya da belki de Apache Koleksiyonlarını tercih ederim.
Sebastian Ganslandt

" yine de koleksiyonu yinelemeye bağlı ", - hayır, öyle değil. Ayrıntılar için cevabıma bakın.
aioobe

3
özel kullanım noktanızda, CrudRepository'yi kendi arabiriminizle, Tekrarlanabilir <T> yerine Koleksiyon <T> / Liste <T> / Set <T> (gerektiği gibi) döndüren yöntemlerle genişletebilirsiniz
Kevin Van Dyck

Yanıtlar:


387

İle Guava kullanabileceğiniz Lists.newArrayList (iterable) veya Sets.newHashSet (iterable) diğer benzer yöntemleri arasında. Bu elbette tüm öğeleri belleğe kopyalar. Bu kabul edilebilir değilse, bence bu ile çalışan kod Iterableyerine almak gerekir Collection. Guava, Collectionbir Iterable( Iterables.isEmpty(Iterable)veya gibi Iterables.contains(Iterable, Object)) kullanarak yapabileceğiniz şeyleri yapmak için uygun yöntemler sağlar , ancak performans sonuçları daha açıktır.


1
Tüm unsurları doğrudan tekrarlar mı? Yani, Lists.newArrayList(Iterable).clear()doğrusal veya sabit zamanlı bir işlem midir?
aioobe

2
@aioobe: Yinelenebilir bir kopyasını oluşturur. Bir görüşün istendiği belirtilmedi ve Collectionya bir yöntemin görünüşü için uygulanamayacağı Iterableya da etkili olmayacağı düşünüldüğünde, bunu yapmak benim için pek bir anlam ifade etmiyor.
ColinD

@ColinD bir görünüm istersem ne olur? Aslında, istediğim başka bir öğeye kaynak koleksiyonu eklemenin sonucu olan bir Koleksiyon görünümüdür. Kullanabilirim Iterables.concat()ama bu bir Iterable, değil bir verir Collection:(
Hendy Irawan

1
Bu benim sorum: stackoverflow.com/questions/4896662/… . Maalesef sorunu çözmeyen basit cevap kullanmaktır Iterables.concat(). Daha uzun cevap veriyor Collection... Bunun neden daha yaygın olarak desteklenmediğini merak ediyorum?
Hendy Irawan

365

JDK 8+ sürümünde, ek kitap kullanmadan:

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);

Edit: Yukarıdaki bir içindir Iterator. Eğer uğraşıyorsanız Iterable,

iterable.forEach(target::add);

86
Veyaiterable.forEach(target::add);
Cephalopod

92

Bunun için kendi yardımcı yönteminizi de yazabilirsiniz:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}

33
1 gidiş varsa Iterableiçin Collectiontek endişe, ben büyük bir 3. parti koleksiyonları-kütüphane ithal üzerinde bu yaklaşımı tercih ediyorum.
aioobe

2
% 99'unun kullanılmadığı 2 MB derlenmiş kütüphane koduna göre 4 satır fonksiyon kodu çok daha fazla tercih edilir. Başka bir maliyet daha var: lisanslama komplikasyonları. Apache 2.0 lisansı esnektir, ancak sıkıcı yetkileri yoktur. İdeal olarak , bu ortak kalıplardan bazılarının doğrudan Java çalışma zamanı kitaplıklarına entegre edildiğini görürüz .
Jonathan Neufeld

2
Bir nokta daha, her nasılsa bir ArrayList kullandığınız için, neden bunun yerine kovaryant Liste türüyle gitmiyorsunuz? Bu, aşağı döküm veya yeniden oluşturma olmadan daha fazla sözleşmeyi yerine getirmenizi sağlar ve Java'nın daha düşük tip sınırları için hiçbir desteği yoktur.
Jonathan Neufeld

@JonathanNeufeld ya da neden sadece bir ArrayList <T> döndürmüyorsunuz?
Juan

5
@Juan Çünkü bu çok katı değil . Bir ArrayList, büyük olasılıkla gereksiz olan (YAGNI), tek sorumluluk ve bağımlılık tersine çevirme ilkelerini ihlal eden uygulama ayrıntılarını ortaya koyar. Listede bırakırım çünkü Koleksiyon tamamen SOLID'de kalırken biraz daha fazlasını ortaya çıkarır. INVOKEINTERFACE kodunun INVOKEVIRTUAL üzerindeki JVM performans etkisinden endişe ediyorsanız, pek çok kıyaslama, uyku kaybına değmeyeceğini ortaya çıkaracaktır.
Jonathan Neufeld

80

Kullanarak Java 8 ile özlü çözüm java.util.stream:

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}

1
Bu yaklaşım yol çok yavaş karşılaştırılır IteratorUtilsdencommons-collections
Alex Burdusel

3
Ne kadar yavaş? IteratorUtils.toList()yeni oluşturulmuş bir listeye öğeleri tek tek eklemek için yineleyiciyi Java 5 öncesi bir şekilde kullanır. Basit ve muhtemelen en hızlı, ancak ikili dosyaya 734 kB ekler ve bu yöntemi en iyi bulduysanız, kendi başınıza yapabilirsiniz.
xehpuk

8
Bazen ilkinin daha hızlı, bazen ikincisinin daha hızlı olduğu konusunda ilkel bir kıyaslama yaptım. Karşılaştırmanızı gösterin.
xehpuk

bu prob yeni kabul edilebilir bir cevap olabilir - Aşırı libs (Guava gibi) önlemek güzel.
java-addict301

48

IteratorUtilsdan commons-collections(onlar son kararlı sürümü 3.2.1 de jeneriği desteklemez rağmen) mayıs yardım:

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());

Sürüm 4.0 (şu anda SNAPSHOT'ta bulunan) jenerikleri desteklemektedir ve kurtulabilirsiniz @SuppressWarnings.

Güncelleme: Kontrol IterableAsListdan Cactoos .


5
Ama bu bir
Yinelemeli

5
@hithwen, anlamıyorum - Yinelenebilir bir yineleyici sağlar (cevapta ayrıntılı olarak belirtilmiştir) - sorun nedir?
Tom

Ne düşündüğümü bilmiyorum ^^ U
hithwen

2
4.1'den beri de IterableUtils.toList(Iterable), bir kolaylık yöntemi olan ve IteratorUtilskaputun altında kullanılan, ancak aynı zamanda null güvenli (aksine IteratorUtils.toList) vardır.
Yoory N.7

21

Gönderen CollectionUtils :

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())

İşte bu yardımcı programın tüm kaynakları:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}

Tüm unsurları doğrudan tekrarlar mı? Yani, Lists.newArrayList(someIterable).clear()doğrusal veya sabit zamanlı bir işlem midir?
aioobe

Kaynak kodunu addAll, adından da anlaşılacağı gibi, yineleyici değerlerini birbiri ardına kopyalar; görünümden ziyade bir kopya oluşturur.
Tomasz Nurkiewicz

Ne yazık ki CollectionUtils, koleksiyonun ekstra bir satırda oluşturulmasını atlamanın hiçbir yöntemi yok .
Karl Richter

Broken Link ☝️☝️
Hola Soy Edu Feliz Navidad

14

O sırada, tüm koleksiyonların sonlu olduğunu unutmayın, Yinelenebilir'in hiçbir vaadi yoktur. Eğer bir şey yinelenebilir ise bir yineleyici alabilirsiniz ve hepsi bu.

for (piece : sthIterable){
..........
}

genişletilecek:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}

it.hasNext () işlevinin hiçbir zaman false döndürmesi gerekmez. Böylece, genel durumda, her Yinelenebiliri bir Koleksiyona dönüştürmeyi bekleyemezsiniz. Örneğin, tüm pozitif doğal sayılar üzerinde tekrarlayabilir, içinde aynı sonuçları tekrar tekrar üreten döngüler içeren bir şey üzerinde tekrarlayabilirsiniz.

Aksi takdirde: Atrey'in cevabı gayet iyi.


1
Hiç kimse pratikte / gerçek kodda sonsuz bir şey (cevapta verilen doğal sayılar örneği gibi) üzerinde tekrarlayan bir Yinelenebilir ile karşılaştı mı? Böyle bir Yinelemenin birçok yerde acıya ve sıkıntıya neden olacağını düşünürdüm ... :)
David

2
@David Üretim kodlarımdan herhangi birinde sonsuz bir Yineleyiciye işaret edemesem de, meydana gelebilecek durumları düşünebilirim. Bir video oyunu, yukarıdaki cevabın önerdiği döngüsel düzende öğeler oluşturan bir yeteneğe sahip olabilir. Sonsuz yineleyicilerle karşılaşmasam da, belleğin gerçek bir endişe olduğu yineleyicilerle kesinlikle karşılaştım. Diskteki dosyalar üzerinde yineleyiciler var. Tam 1 TB'lık diskim ve 4GB ram'ım varsa, yineleyicimi bir Koleksiyona dönüştüren belleğim tükenebilir.
radicaledward101

14

Çok kullanıyorum FluentIterable.from(myIterable).toList().


9
Bunun da Guava'dan olduğuna dikkat edilmelidir.
Vadzim

Veya org.apache.commons.collections4. Sonra FluentIterable.of (myIterable) .toList ()
du-it

9

Bu, sorunuzun bir cevabı değil, ama bunun sorununun çözümü olduğuna inanıyorum. Arayüz org.springframework.data.repository.CrudRepositorygerçekten dönen yöntemlere sahiptir, java.lang.Iterableancak bu arayüzü kullanmamalısınız. Bunun yerine sizin durumunuzda alt arayüzler kullanın org.springframework.data.mongodb.repository.MongoRepository. Bu arabirim, türdeki nesneleri döndüren yöntemlere sahiptir java.util.List.


2
Kodunuzu somut bir uygulamaya bağlamadan kaçınmak için genel CrudRepository kullanarak tanıtacağım.
15'de stanlick

7

Varsa, mevcut bir Koleksiyonu yayınlamak için özel yardımcı programımı kullanıyorum.

Ana:

public static <T> Collection<T> toCollection(Iterable<T> iterable) {
    if (iterable instanceof Collection) {
        return (Collection<T>) iterable;
    } else {
        return Lists.newArrayList(iterable);
    }
}

İdeal olarak yukarıda belirtilenler ImmutableList kullanır, ancak ImmutableCollection istenmeyen sonuçlar verebilecek boş değerlere izin vermez.

Testler:

@Test
public void testToCollectionAlreadyCollection() {
    ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
    assertSame("no need to change, just cast", list, toCollection(list));
}

@Test
public void testIterableToCollection() {
    final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);

    Collection<String> collection = toCollection(new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return expected.iterator();
        }
    });
    assertNotSame("a new list must have been created", expected, collection);
    assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}

Koleksiyonların tüm alt türleri (Set, List, vb.) İçin benzer yardımcı programlar uygularım. Bunların zaten Guava'nın bir parçası olacağını düşünürdüm, ama bulamadım.


1
Yıllık cevabınız, çok sayıda görüş ve yorum çeken yeni bir sorunun temelini oluşturuyor stackoverflow.com/questions/32570534/… .
Paul Boddington

6

En kısa zamanda dediğimiz gibi contains, containsAll, equals, hashCode, remove, retainAll, sizeveya toArray, yine unsurları çapraz gerekirdi.

Zaman zaman yalnızca böyle yöntemleri çağırıyorsanız isEmptyveya cleartembel bir şekilde koleksiyon oluşturarak daha iyi olacağını düşünürseniz. Örneğin, ArrayListdaha önce yinelenen öğeleri depolamak için bir desteğiniz olabilir .

Herhangi bir kütüphanede böyle bir sınıf bilmiyorum, ama yazmak oldukça basit bir alıştırma olmalı.



6

Java 8'de bir gelen tüm unsurları eklemek için bunu yapabilirsiniz Iterableiçin Collectionve iade:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
  Collection<T> collection = new ArrayList<>();
  iterable.forEach(collection::add);
  return collection;
}

@Afreys yanıtından esinlenmiştir.


5

RxJava bir çekiç olduğundan ve bu bir çivi gibi göründüğünden,

Observable.from(iterable).toList().toBlocking().single();

23
jquery belki dahil etmenin bir yolu var mı?
Dmitry Minkovsky

3
RxJava'da null öğe varsa çöküyor. değil mi?
MBH

RxJava2 boş öğeler izin vermez, RxJava iyi olması gerektiğine inanıyorum.
DariusL

4

Java 8'de bunu yapmanın harika bir yolu için bir SSCCE

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class IterableToCollection {
    public interface CollectionFactory <T, U extends Collection<T>> {
        U createCollection();
    }

    public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
        U collection = factory.createCollection();
        iterable.forEach(collection::add);
        return collection;
    }

    public static void main(String[] args) {
        Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
        HashSet<Integer> hashSet = collect(iterable, HashSet::new);
        LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
    }
}

2

İki açıklama

  1. Her döngü için kullanmak için Yinelenebilir Koleksiyonu Koleksiyona dönüştürmeye gerek yoktur - Yinelenebilir doğrudan bu döngüde kullanılabilir, sözdizimsel bir fark yoktur, bu yüzden orijinal sorunun neden sorulduğunu anlayamıyorum.
  2. Yinelenebilir öğeyi Koleksiyona dönüştürmenin önerilen yolu güvensizdir (aynısı CollectionUtils ile ilgilidir) - next () yöntemine yapılan sonraki çağrıların farklı nesne örnekleri döndürdüğünün garantisi yoktur. Dahası, bu endişe saf teorik değildir. Örneğin, değerleri bir Hadoop Redüktör azaltma yöntemine aktarmak için kullanılan yinelenebilir uygulama, yalnızca farklı alan değerleriyle her zaman aynı değer örneğini döndürür. Bu nedenle, makeCollection uygulamasını yukarıdan (veya CollectionUtils.addAll (Yineleyici)) uygularsanız, tüm özdeş öğeleri içeren bir koleksiyon elde edersiniz.

2

Arayüzde bildirilen varsayılandan ziyade List, Projects'yi almaya çalışırken benzer bir durumla karşılaştım . Yani, arayüzümden (ki bu da uzanıyor ), sadece yerine dönmek için yöntemi beyan ettim .Iterable<T> findAll()CrudRepositoryProjectRepositoryCrudRepositoryfindAll()List<Project>Iterable<Project>

package com.example.projectmanagement.dao;

import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface ProjectRepository extends CrudRepository<Project, Long> {

    @Override
    List<Project> findAll();
}

Bence, en basit çözüm, dönüştürme mantığı veya harici kütüphanelerin kullanımına gerek kalmadan.



1

Herhangi bir bağımlılığı olmayan basit bir tek çözüm görmedim . Basit kullanım

List<Users> list;
Iterable<IterableUsers> users = getUsers();

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());

0

Eclipse Collections fabrikalarını kullanabilirsiniz :

Iterable<String> iterable = Arrays.asList("1", "2", "3");

MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);

Ayrıca dönüştürebilirsiniz Iterablebir etmek LazyIterableve dönüştürücü yöntemlerini veya mevcut diğer kullanılabilir API'leri herhangi birini kullanabilirsiniz.

Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);

MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();

Yukarıdaki Mutabletiplerin tümü uzanırjava.util.Collection .

Not: Eclipse Collections için bir komisyoncuyum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.