Tại sao tôi không thể ánh xạ số nguyên thành chuỗi khi truyền trực tuyến từ một mảng?


92

Mã này hoạt động (được lấy trong Javadoc):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));

Không thể biên dịch cái này:

int[] numbers = {1, 2, 3, 4};
String commaSeparatedNumbers = Arrays.stream(numbers)
    .map((Integer i) -> i.toString())
    .collect(Collectors.joining(", "));

IDEA cho tôi biết tôi có một "kiểu trả về không tương thích Chuỗi trong biểu thức lambda".

Tại sao ? Và làm thế nào để khắc phục điều đó?

Câu trả lời:


114

Arrays.stream(int[])tạo ra một IntStream, không phải a Stream<Integer>. Vì vậy, bạn cần gọi mapToObjthay vì chỉ map, khi ánh xạ mộtint một đối tượng.

Điều này sẽ hoạt động như mong đợi:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(i -> ((Integer) i).toString()) //i is an int, not an Integer
    .collect(Collectors.joining(", "));

mà bạn cũng có thể viết:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));

3
Sự khác biệt giữa IntStreamvà là Stream<Integer>gì?
Florian Margaine

8
@FlorianMargaine An IntStreamlà một chuyên môn hóa dòng cho các intgiá trị nguyên thủy . A Stream<Integer>chỉ là một Integerđối tượng lưu giữ Stream .
Alexis C.

2
@FlorianMargaine IntStreamlà một luồng hoặc nguyên thủy (ints) trong khi Steram<Integer>là một luồng đối tượng. Các luồng gốc có các phương pháp chuyên biệt vì lý do hiệu suất.
assylias

7
IntStream.mapToObjmong đợi một IntFunction, một hàm sử dụng một intgiá trị, do đó  .mapToObj((Integer i) -> i.toString())không hoạt động. Nó sẽ không được đề xuất dù sao vì nó chứa một chuyển đổi không cần thiết từ intsang Integer. Ngược lại, .mapToObj(Integer::toString)hoạt động tốt vì nó sẽ gọi staticphương thức Integer.toString(int). Lưu ý rằng điều này khác với cách gọi .map(Integer::toString)a  Stream<Integer>vì cái sau sẽ không biên dịch vì nó không rõ ràng.
Holger

1
@cic: không, việc gọi .map(Integer::toString)a Stream<Integer>thực sự mơ hồ vì cả hai .map(i->i.toString()).map(i->Integer.toString(i))đều hợp lệ. Nhưng nó có thể dễ dàng giải quyết bằng cách sử dụng .map(Object::toString).
Holger

19

Arrays.stream(numbers)tạo một ẩn IntStreamvà hoạt động bản đồ trên một IntStreamyêu cầu một IntUnaryOperator(tức là một hàmint -> int ). Hàm ánh xạ bạn muốn áp dụng không tôn trọng hợp đồng này và do đó lỗi biên dịch.

Bạn sẽ cần phải gọi boxed()trước để nhận được Stream<Integer>(đây là thông tin Arrays.asList(...).stream()trả về). Sau đó chỉ cần gọimap như bạn đã làm trong đoạn mã đầu tiên.

Lưu ý rằng nếu bạn cần boxed()theo dõi, mapbạn có thể muốn sử dụngmapToObj trực tiếp.

Lợi thế là mapToObj không yêu cầu đóng hộp từng intgiá trị cho một Integerđối tượng; tất nhiên tùy thuộc vào chức năng ánh xạ mà bạn áp dụng; vì vậy tôi sẽ đi với tùy chọn này cũng ngắn hơn để viết.


5

Bạn có thể tạo một Integer Stream bằng Arrays.stream (int []), bạn có thể gọi mapToObjnhư thế mapToObj(Integer::toString).

String csn = Arrays.stream(numbers) // your numbers array
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));

Hi vọng điêu nay co ich..


2

Không có quyền anh, AFAIK và không có sự bùng nổ của các chuỗi nhỏ được thêm vào đống:

public static void main(String[] args) {
    IntStream stream = IntStream.of(1, 2, 3, 4, 5, 6);
    String s = stream.collect(StringBuilder::new, (builder, n) -> builder.append(',').append(n), (x, y) -> x.append(',').append(y)).substring(1);
    System.out.println(s);
}

1

Nếu mục đích của mẫu và câu hỏi này là tìm ra cách ánh xạ các chuỗi với một luồng int (ví dụ: sử dụng một luồng int để truy cập một chỉ mục trong Mảng chuỗi), bạn cũng có thể sử dụng quyền anh, sau đó truyền sang một int (sau đó sẽ cho phép truy cập chỉ mục của mảng).

int[] numbers = {0, 1, 2, 3}; 
String commaSeparatedNumbers = Arrays.stream(numbers)
    .boxed()
    .map((Integer i) -> Integer.toString((int)i))
    .collect(Collectors.joining(", "));

Lời gọi .boxed () chuyển đổi IntStream của bạn (một luồng các int nguyên thủy) thành Luồng (một luồng đối tượng - cụ thể là các đối tượng Số nguyên), sau đó sẽ chấp nhận trả về của một đối tượng (trong trường hợp này là đối tượng Chuỗi) từ lambda của bạn. Ở đây, nó chỉ là một biểu diễn chuỗi của số cho mục đích trình diễn, nhưng nó có thể dễ dàng (và thực tế hơn) là bất kỳ đối tượng chuỗi nào - giống như phần tử của một mảng chuỗi như đã đề cập trước đây.

Tôi chỉ nghĩ rằng tôi sẽ đưa ra một khả năng khác. Trong lập trình, luôn có nhiều cách để hoàn thành một nhiệm vụ. Biết càng nhiều càng tốt, sau đó chọn cái phù hợp nhất cho nhiệm vụ hiện tại, lưu ý các vấn đề về hiệu suất, tính trực quan, sự rõ ràng của mã, sở thích của bạn trong phong cách mã hóa và tự ghi lại nhiều nhất.

Chúc bạn viết mã vui vẻ!


1
Bạn đang làm những công việc không cần thiết. Bạn đóng hộp mỗi intloại theo loại trình bao bọc của nó Integervà bạn mở hộp ngay sau đó.
Alexis C.
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.