Làm cách nào để giữ thứ tự lặp lại của Danh sách khi sử dụng Collections.toMap () trên luồng?


82

Tôi đang tạo một Maptừ một Listnhư sau:

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

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

Tôi muốn giữ cùng một thứ tự lặp lại như trong List. Làm cách nào tôi có thể tạo một LinkedHashMapbằng các Collectors.toMap()phương pháp?


1
xin vui lòng kiểm tra câu trả lời của tôi dưới đây. Đó là chỉ có 4 dòng mã sử dụng một tùy chỉnh Supplier, AccumulatorCombinercho các collectphương pháp của bạn stream:)
hzitoun

1
Câu hỏi đã được trả lời, tôi chỉ muốn tìm ra con đường để tìm câu trả lời cho câu hỏi này. (1) Bạn muốn có thứ tự trong bản đồ, bạn phải sử dụng LinkedHashMap (2) Collectors.toMap () có nhiều cách triển khai, một trong số yêu cầu Bản đồ. Vì vậy, hãy sử dụng Bản đồ liên kết nơi nó mong đợi Bản đồ.
Satyendra Kumar

Câu trả lời:


116

Các phiên bản 2 tham số củaCollectors.toMap() sử dụng 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);
}

Để sử dụng phiên bản 4 tham số , bạn có thể thay thế:

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

với:

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

Hoặc để làm cho nó gọn gàng hơn một chút, hãy viết một toLinkedMap()phương thức mới và sử dụng:

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
Tại sao lại phức tạp như vậy? bạn có thể dễ dàng làm điều đó, kiểm tra câu trả lời của tôi dưới đây
hzitoun

1
mergeFunctionrằng trong phiên bản 4 tham số Collectors.toMap()không có manh mối nào về khóa nào đang được hợp nhất, cả hai uvđều là các giá trị. Vì vậy, thông báo về IllegalStateException là không đúng.
MoonFruit

1
@hzitoun bởi vì nếu bạn chỉ sử dụng Map::put, bạn sẽ có (có thể) các giá trị khác nhau cho cùng một khóa. Bằng cách sử dụng Map::put, bạn đã gián tiếp chọn rằng giá trị đầu tiên bạn gặp là không chính xác. Đó có phải là những gì người dùng cuối muốn? Bạn có chắc không? Nếu có, thì chắc chắn, hãy sử dụng Map::put. Nếu không, bạn không chắc chắn và không thể quyết định: hãy cho người dùng biết rằng luồng của họ được ánh xạ tới hai khóa giống nhau với các giá trị khác nhau. Tôi đã nhận xét về câu trả lời của riêng bạn, nhưng nó hiện đã bị khóa.
Olivier Grégoire

68

Cung cấp của riêng bạn Supplier, AccumulatorCombiner:

    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 Câu trả lời được chấp nhận cho phép sử dụng lại logic
xy-

1
Luồng dựa trên các tác dụng phụ như map.put(..)là sai.
Nikolas Charalambidis

Bạn có biết cái gì nhanh hơn không? Sử dụng phiên bản của bạn hoặc câu trả lời được chấp nhận?
nimo23

@Nikolas bạn có thể vui lòng giải thích những gì bạn có nghĩa là với tác dụng phụ ? Tôi thực sự có vấn đề khi quyết định chọn cái nào: stackoverflow.com/questions/61479650/…
nimo23

0

Trong Kotlin, toMap()là bảo tồn trật tự.

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

Trả về một bản đồ mới chứa tất cả các cặp khóa-giá trị từ tập hợp các cặp đã cho.

Bản đồ được trả về bảo toàn thứ tự lặp lại mục nhập của tập hợp ban đầu. Nếu bất kỳ cặp nào trong hai cặp có cùng khóa, cặp cuối cùng sẽ được thêm vào bản đồ.

Đây là cách triển khai của nó:

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()
}

Cách sử dụng chỉ đơn giản là:

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

Tập hợp cơ bản cho maplà a LinkedHashMap(được sắp xếp theo thứ tự chèn).


0

Chức năng đơn giản để ánh xạ mảng đối tượng theo một số trường:

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()
);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.