2015-03-19 13 views
28

Tôi đã tự hỏi một mã như sau:Có cách nào tránh vòng lặp khi thêm vào danh sách không?

List<String> list = new ArrayList<String>(); 
for(CustomObject co : objects) { 
    list.add(co.getActualText()); 
} 

Có thể viết nó khác đi không? Ý tôi là dĩ nhiên tại một số điểm sẽ có một vòng lặp nhưng tôi tự hỏi nếu có một cách sử dụng API Tôi phớt lờ

+1

nó được gọi là Clojure; D Bạn có thể đọc về (conj) tại đây: http://clojuredocs.org/clojure.core/conj – sova

+2

nó còn được gọi là Scala, và Haskell, và… (chèn tên của bất kỳ ngôn ngữ lập trình mạnh mẽ nào) –

Trả lời

26

Nếu bạn sử dụng Java 8, bạn có thể tận dụng các API Suối:

List<String> list = objects.stream() 
          .map(CustomObject::getActualText) 
          .collect(Collectors.toList()); 
+15

TBH IMHO điều này thực sự không thể đọc được cho một tác vụ đơn giản. Đây không phải là câu trả lời của bạn, nhưng về cách tiếp cận Java chính nó – Jim

+9

Không thực sự đồng ý với Jim, nó cũng là vấn đề làm quen với một khái niệm mới. Mặc dù trong Scala nó sẽ đẹp hơn với một danh sách val đơn giản = objects.map (_. GetActualText) –

+7

Những người .NET có LINQ trong nhiều năm nay và rõ ràng rằng nó là một cách tiếp cận tốt hơn nhiều so với các vòng xây dựng danh sách trong hầu hết Mọi trường hợp. Tôi nghĩ rằng người Java chỉ cần đi xung quanh để có một sự thay đổi trong tâm lý ở đây. Mã này không thể đọc được. Nó không thể đọc được với những người thiếu kinh nghiệm. Đây là trạng thái tạm thời. – usr

20

Nếu bạn có Java 8, điều gì về:

objects.forEach(item -> list.add(item.getActualText())); 

Nội bộ vẫn là một vòng lặp.

CHỈNH SỬA một chút Off-Topic: IMO Đây là giải pháp tốt nhất và dễ đọc nhất. Tại sao không chỉ sử dụng một foreach bạn có thể yêu cầu. Câu trả lời: Bởi vì cách này bộ sưu tập chọn cách tốt nhất để lặp qua các mục. Ví dụ, ArrayList không sử dụng một iterator, bởi vì nó biết tốt hơn so với bạn:

@Override 
public void forEach(Consumer<? super E> action) { 
    Objects.requireNonNull(action); 
    final int expectedModCount = modCount; 
    @SuppressWarnings("unchecked") 
    final E[] elementData = (E[]) this.elementData; 
    final int size = this.size; 
    for (int i=0; modCount == expectedModCount && i < size; i++) { 
     action.accept(elementData[i]); 
    } 
    if (modCount != expectedModCount) { 
     throw new ConcurrentModificationException(); 
    } 
} 
+0

Tôi muốn thêm rằng tôi đã phát triển theo phương pháp "chức năng" mà Konstantin Yovkov đã trình bày. Phong cách cảm thấy rất sạch sẽ, ngay cả khi nó trông khủng khiếp và có thể hoạt động kém trong java – Felk

3

Sử dụng con suối sẽ nhiều thành ngữ trong Java 8, nhưng nếu bạn muốn nó sẽ được gần gũi hơn với cách tiếp cận dựa trên vòng lặp thông thường bạn có thể sử dụng forEach:

objects.forEach(co -> list.add(co.getActualText())); 
6

Mặc dù rõ ràng một chút gợi ý vô lý: bạn có thể tránh các vòng bằng cách thêm chúng một cách đệ quy.

void add(List<? super String> receiver, CustomObject[] objects) { 
    addRec(receiver, toAdd, 0, objects.length()); 
} 

void addRec(List<? super String> receiver, CustomObject[] objects, int start, int end) { 
    if (start + 1 == end) { 
    receiver.add(objects[start].getActualText()); 
    } else if (start != end) { 
    int mid = (start + end)/2; 
    addRec(receiver, objects, start, mid); 
    addRec(receiver, objects, mid, end); 
    } 
} 
+0

Vì các hoạt động O (n) thực sự yêu cầu dòng điều khiển thay đổi động (tức là vòng lặp), đây sẽ là câu trả lời của tôi. Đó là một chút trollish trong một ngôn ngữ bắt buộc, nhưng trong một ngôn ngữ chức năng tinh khiết, đó là tất cả các bạn đã có. +1. – imallett

9

Tất nhiên, Apache Commons và ổi cũng cung cấp cách để tránh các vòng lặp mà không sử dụng Java 8.

Commons CollectionUtils.collect:

CollectionUtils.collect(objects, Transformer.invokerTransformer("getActualText"), list); 

Ổi Lists.transform:

List<String> list = Lists.transform(objects, 
    new Function<CustomObject, String>() { 
     public String apply(CustomObject co) { 
      return co.getActualText(); 
     } 
    } 
); 
+2

Trong Java 8, ví dụ Guava có thể được viết ngắn gọn hơn bằng cách thay thế lớp ẩn danh bằng lambda: 'Lists.transform (objects, (co) -> co.getActualText())' – meriton

4

Nếu bạn sử dụng Eclipse Collections (trước đây là GS Collections), bạn có thể viết như sau trong Java 8:

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(CustomObject::getActualText); 

Với Java 5-7 bạn có thể sử dụng một lớp bên trong vô danh đại diện cho SAM loại Function với phương pháp thu thập.

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(new Function<CustomObject, String>() { 
    public String valueOf(CustomObject object){ 
     return object.getActualText(); 
    } 
}); 

Lưu ý: Tôi là một người có duyên cho Eclipse Collections

0

Để đạt được hiệu quả thực sự tốt khi sao chép một loạt các dữ liệu giữa hai loại danh sách chưa được biết đến nhau, thì phải có một cơ chế theo đó một loại "đáng tin cậy" có thể yêu cầu mỗi để lộ (các) mảng sao lưu được liên kết với một loạt các phần tử, và sau đó sử dụng thao tác sao chép số lượng lớn để di chuyển dữ liệu từ một đến khác. Có thể viết một lớp như vậy hoàn toàn bằng Java, bằng cách có một phương thức GetArraySource truyền cho hàm tạo của lớp ArraySource tin cậy một đối tượng mà nó có thể sử dụng để yêu cầu mảng sao lưu được liên kết với một phần tử cụ thể (trả về sẽ bao gồm mảng sao lưu) và phạm vi các yếu tố được bao gồm trong đó).Mã muốn sao chép sẽ gọi GetArraySource và vượt qua ArraySource được trả về từ đó đến phương thức CopyFromArraySource của danh sách đích, sau đó có thể yêu cầu ArraySource sao chép một hoặc nhiều phạm vi mục vào (các) mảng sao lưu riêng của nó.

Nếu ArraySource là một lớp học được cung cấp với Java, và Oracle ghi nhận chính xác những gì nó sẽ làm gì với mảng mà nó nhận được, sau đó nó sẽ có thể với nhiều loại như ArrayListString để lộ nội dung của họ như là một ArraySource, hoặc chấp nhận dữ liệu bên ngoài từ một số ArraySource, mà không hiển thị không đúng mảng của chúng với bất kỳ mã nào có thể lạm dụng nó.

Thật không may, trừ khi Oracle kết hợp một thứ như vậy vào bản phân phối Java, hỗ trợ có thể sẽ quá thưa thớt để hữu ích. Nó không tốt để có danh sách nguồn hỗ trợ một lớp như vậy, đích hỗ trợ một lớp khác và mã muốn hoạt động sao chép thứ ba. Tất cả ba lớp cần hỗ trợ cùng lớp tin cậy-array-segment-copy-helper.

+0

Tôi nghĩ bạn có thứ gì đó giống như C's memcopy trong tâm trí của dữ liệu thực tế. Đúng? – Jim

+0

@Jim: Tôi hy vọng rằng các hoạt động sao chép số lượng lớn trong 'Mảng' có thể được thực thi bằng cách sử dụng cái gì đó tương đương với' memcpy' khi nó xác định rằng không có đối tượng nào có thể tồn tại trong mảng nguồn mà loại mảng đích không thể giữ [một nỗ lực để ví dụ các bản sao chép số lượng lớn từ một 'Cat []' thành một 'Animal []' có thể chỉ đơn giản là sao chép các tham chiếu một cách trực tiếp, nhưng đi theo cách khác nó sẽ là cần thiết để xác nhận loại của từng đối tượng được tham chiếu trước khi sao chép tham chiếu]. – supercat

Các vấn đề liên quan