Bir akışta Collections.toMap () kullanırken Listenin yineleme sırasını nasıl koruyabilirim?


84

Ben oluşturma Mapbir gelen Listaşağıdaki gibi:

List<String> strings = Arrays.asList("a", "bb", "ccc");

Map<String, Integer> map = strings.stream()
    .collect(Collectors.toMap(Function.identity(), String::length));

İle aynı yineleme sırasını korumak istiyorum List. Yöntemleri LinkedHashMapkullanarak nasıl oluşturabilirim Collectors.toMap()?


1
lütfen aşağıdaki cevabımı kontrol edin. Bu bir özel kullanarak kod sadece 4 hatları Supplier, Accumulatorve Combineriçin collectsenin yöntemine stream:)
hzitoun

1
Soru zaten cevaplandı, sadece bu sorunun cevabını bulmanın yolunu belirlemek istiyorum. (1) Haritada sipariş istiyorsun, LinkedHashMap kullanmalısın (2) Collectors.toMap () birçok uygulamaya sahip, biri bir Harita ister. Bu yüzden Map'in beklediği yerde LinkedHashMap kullanın.
Satyendra Kumar

Yanıtlar:


121

2-parametreli versiyonuCollectors.toMap() bir kullanır HashMap:

public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
    Function<? super T, ? extends K> keyMapper, 
    Function<? super T, ? extends U> valueMapper) 
{
    return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

4 parametreli versiyonu kullanmak için şunları değiştirebilirsiniz:

Collectors.toMap(Function.identity(), String::length)

ile:

Collectors.toMap(
    Function.identity(), 
    String::length, 
    (u, v) -> {
        throw new IllegalStateException(String.format("Duplicate key %s", u));
    }, 
    LinkedHashMap::new
)

Ya da biraz daha temiz hale getirmek için yeni bir toLinkedMap()yöntem yazın ve bunu kullanın:

public class MoreCollectors
{
    public static <T, K, U> Collector<T, ?, Map<K,U>> toLinkedMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends U> valueMapper)
    {
        return Collectors.toMap(
            keyMapper,
            valueMapper, 
            (u, v) -> {
                throw new IllegalStateException(String.format("Duplicate key %s", u));
            },
            LinkedHashMap::new
        );
    }
}

2
Neden bu kadar karmaşıklık? bunu kolayca yapabilirsin, aşağıdaki cevabımı kontrol et
hzitoun

1
mergeFunction4-parametre versiyonu olduğu Collectors.toMap()anahtar birleştirilmiş iki edildiği hakkında hiçbir ipucu sahiptir uve vdeğerlerdir. Dolayısıyla IllegalStateException ile ilgili mesaj o kadar doğru değil.
MoonFruit

1
@hzitoun çünkü basitçe kullanırsanız Map::put, aynı anahtar için (muhtemelen) farklı değerler elde edersiniz. Kullanarak Map::put, dolaylı olarak karşılaştığınız ilk değerin yanlış olduğunu seçtiniz. Son kullanıcının istediği bu mu? Emin misiniz? Eğer evet ise, o zaman kesinlikle kullanın Map::put. Aksi takdirde, emin olamazsınız ve karar veremezsiniz: Kullanıcıya akışlarının farklı değerlere sahip iki özdeş anahtarla eşlendiğini bildirin. Kendi cevabınız üzerine yorum yapardım, ancak şu anda kilitli.
Olivier Grégoire

70

Kendi sağlayın Supplier, Accumulatorve Combiner:

    List<String> myList = Arrays.asList("a", "bb", "ccc"); 
    // or since java 9 List.of("a", "bb", "ccc");
    
    LinkedHashMap<String, Integer> mapInOrder = myList
        .stream()
        .collect(
            LinkedHashMap::new,                                   // Supplier
            (map, item) -> map.put(item, item.length()),          // Accumulator
            Map::putAll);                                         // Combiner

    System.out.println(mapInOrder);  // {a=1, bb=2, ccc=3}

1
@Sushil Kabul edildi cevabı mantığın yeniden kullanılmasına izin veriyor
silindir

1
Akış, map.put(..)hangisinin yanlış olduğu gibi yan etkilere dayanır .
Nikolas Charalambidis

Neyin daha hızlı olduğunu biliyor musun? Kendi versiyonunuzu mu yoksa kabul edilen cevabı mı kullanıyorsunuz?
nimo23

@Nikolas yan etkilerle ne demek istediğinizi açıklar mısınız? Aslında hangisini seçeceğime karar verme sorunum var: stackoverflow.com/questions/61479650/…
nimo23

0

Kotlin'de toMap()sipariş koruma var.

fun <K, V> Iterable<Pair<K, V>>.toMap(): Map<K, V>

Verilen çift koleksiyonundaki tüm anahtar / değer çiftlerini içeren yeni bir harita döndürür.

Döndürülen harita, orijinal koleksiyonun giriş yineleme sırasını korur. İki çiftten herhangi biri aynı anahtara sahipse, sonuncusu haritaya eklenir.

İşte uygulaması:

public fun <K, V> Iterable<Pair<K, V>>.toMap(): Map<K, V> {
    if (this is Collection) {
        return when (size) {
            0 -> emptyMap()
            1 -> mapOf(if (this is List) this[0] else iterator().next())
            else -> toMap(LinkedHashMap<K, V>(mapCapacity(size)))
        }
    }
    return toMap(LinkedHashMap<K, V>()).optimizeReadOnlyMap()
}

Kullanım basittir:

val strings = listOf("a", "bb", "ccc")
val map = strings.map { it to it.length }.toMap()

İçin temel koleksiyon mapbir LinkedHashMap(ekleme sıralı).


0

Nesne dizisini bazı alanlara göre eşlemek için basit işlev:

public static <T, E> Map<E, T> toLinkedHashMap(List<T> list, Function<T, E> someFunction) {
    return list.stream()
               .collect(Collectors.toMap(
                   someFunction, 
                   myObject -> myObject, 
                   (key1, key2) -> key1, 
                   LinkedHashMap::new)
               );
}


Map<String, MyObject> myObjectsByIdMap1 = toLinkedHashMap(
                listOfMyObjects, 
                MyObject::getSomeStringField()
);

Map<Integer, MyObject> myObjectsByIdMap2 = toLinkedHashMap(
                listOfMyObjects, 
                MyObject::getSomeIntegerField()
);
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.