2009-04-10 46 views
24

Tôi đã lớp ViewValue quy định như sau:Java - bản đồ một danh sách các đối tượng vào một danh sách với giá trị tài sản của họ thuộc tính

class ViewValue { 

private Long id; 
private Integer value; 
private String description; 
private View view; 
private Double defaultFeeRate; 

// getters and setters for all properties 
} 

Một nơi nào đó trong mã của tôi cần phải chuyển đổi một danh sách các trường hợp ViewValue đến một danh sách chứa các giá trị của trường id từ ViewValue tương ứng.

tôi nó sử dụng vòng lặp foreach:

List<Long> toIdsList(List<ViewValue> viewValues) { 

    List<Long> ids = new ArrayList<Long>(); 

    for (ViewValue viewValue : viewValues) { 
     ids.add(viewValue.getId()); 
    } 

    return ids; 

}

Có một cách tiếp cận tốt hơn cho vấn đề này?

Trả lời

22

EDIT: Câu trả lời này được dựa trên ý tưởng rằng bạn sẽ cần phải làm những việc tương tự cho các thực thể khác nhau và các thuộc tính khác nhau ở nơi khác trong mã của bạn. Nếu bạn chỉ cần chuyển đổi danh sách ViewValues ​​thành danh sách Longs by ID, sau đó gắn với mã ban đầu của bạn. Tuy nhiên, nếu bạn muốn có giải pháp có thể sử dụng lại nhiều hơn, hãy đọc ...

Tôi sẽ khai báo giao diện cho phép chiếu, ví dụ:

public interface Function<Arg,Result> 
{ 
    public Result apply(Arg arg); 
} 

Sau đó, bạn có thể viết một phương pháp duy nhất generic chuyển đổi:

public <Source, Result> List<Result> convertAll(List<Source> source, 
    Function<Source, Result> projection) 
{ 
    ArrayList<Result> results = new ArrayList<Result>(); 
    for (Source element : source) 
    { 
     results.add(projection.apply(element)); 
    } 
    return results; 
} 

Sau đó, bạn có thể định nghĩa dự báo đơn giản như thế này:

private static final Function<ViewValue, Long> ID_PROJECTION = 
    new Function<ViewValue, Long>() 
    { 
     public Long apply(ViewValue x) 
     { 
      return x.getId(); 
     } 
    }; 

Và áp dụng nó chỉ như thế này:

List<Long> ids = convertAll(values, ID_PROJECTION); 

(Obvi ously sử dụng K & R giằng và đường còn làm cho việc kê khai chiếu ngắn hơn một chút :)

Thẳng thắn mà nói tất cả điều này sẽ đẹp hơn rất nhiều với các biểu thức lambda, nhưng đừng bận tâm ...

+4

giải pháp Rất Javaesque - nhưng không phải trong một cách tốt. Nó không chính xác làm giảm số lượng hoặc sự phức tạp của mã, phải không? –

+1

Tôi muốn nói rằng nó là LINQ-esque hơn Java-esque cá nhân. Tôi muốn tạo mã chuyển đổi và sau đó cô lập khía cạnh chiếu, nhưng nếu bạn muốn sao chép lôgic chuyển đổi tải nhiều lần, điều đó là tốt. Nó sẽ đẹp hơn rất nhiều với các biểu thức lambda, tất nhiên. –

+1

Nó không giống như chúng ta làm với Comparator trong Java? –

2

Điều đó phụ thuộc vào những gì bạn sau đó làm với List<Long>, và List<ViewValue>

Ví dụ, bạn có thể nhận được đầy đủ chức năng từ việc tạo Danh sách thực hiện của riêng bạn mà kết thúc tốt đẹp một List<ViewValue>, thực hiện iterator() với một thực hiện iterator rằng lặp trên các ViewValues, trả lại id.

3

Bạn có thể Ude một wrapper:

public class IdList impements List<Long> 
{ 
    private List<ViewValue> underlying; 

    pubic IdList(List<ViewValue> underying) 
    { 
     this.underlying = underying; 
    } 

    public Long get(int index) 
    { 
     return underlying.get(index).getId() 
    } 

    // other List methods 
} 

dù thats thậm chí tẻ nhạt nhiều việc, nó có thể cải thiện hiệu suất.

Bạn cũng có thể triển khai tính tổng quát của giải pháp và giải pháp của mình bằng cách sử dụng sự phản chiếu, nhưng điều đó rất có hại đối với lỗ thủng.

Không có giải pháp chung ngắn gọn và dễ dàng trong Java, Im sợ. Trong Groovy, bạn chỉ đơn giản là sử dụng collect(), nhưng tôi tin rằng nó cũng liên quan đến sự phản chiếu.

3

Tôi đã triển khai một thư viện chức năng nhỏ cho ứng dụng này.Một trong những phương pháp có chữ ký này:

<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType) 

nào mất chuỗi và sử dụng phản ánh để tạo ra một cuộc gọi đến bất động sản sau đó nó sẽ trả về một danh sách được hỗ trợ bởi các objectList nơi nhận được và iterator thực hiện sử dụng cuộc gọi khách sạn này.

Các chức năng mapToProperty được thực hiện theo chức năng bản đồ chung, có chức năng như một người lập bản đồ, giống như một bài đăng khác được mô tả. Rất hữu ích.

tôi đề nghị bạn đọc lên về lập trình functionl cơ bản và đặc biệt là hãy nhìn vào functors (đối tượng thực hiện một chức năng bản đồ)

Edit: Reflection thực sự không phải là đắt. JVM đã cải thiện rất nhiều trong lĩnh vực này. Chỉ cần chắc chắn để biên dịch các lời gọi một lần và tái sử dụng nó.

Edit2: Mẫu mã

public class MapExample { 
    public static interface Function<A,R> 
    { 
     public R apply(A b); 
    } 

    public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType) 
    { 
     try { 
      final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1)); 

      if(!propertyType.isAssignableFrom(m.getReturnType())) 
       throw new IllegalArgumentException(
        "Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName() 
       ); 

      return new Function<A,R>() 
      { 
       @SuppressWarnings("unchecked") 
       public R apply(A b) 
       { 
        try { 
         return (R)m.invoke(b); 
        } catch (Exception e) { 
         throw new RuntimeException(e); 
        } 
       }; 
      }; 

     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper) 
    { 
     return new AbstractList<T2>() 
     { 
      @Override 
      public T2 get(int index) { 
       return mapper.apply(list.get(index)); 
      } 

      @Override 
      public int size() { 
       return list.size(); 
      } 
     }; 
    } 

    @SuppressWarnings("unchecked") 
    public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType) 
    { 
     if(list == null) 
      return null; 
     else if(list.isEmpty()) 
      return Collections.emptyList(); 

     return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType)); 
    } 
} 
31

Bạn có thể làm điều đó trong một lớp lót bằng Commons BeanUtils và Bộ sưu tập:
(? Tại sao viết mã riêng của bạn khi những người khác đã làm điều đó cho bạn)

import org.apache.commons.beanutils.BeanToPropertyValueTransformer; 
import org.apache.commons.collections.CollectionUtils; 

... 

List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
             new BeanToPropertyValueTransformer("id")); 
+3

+1 cho nhận xét "tại sao viết mã của riêng bạn khi người khác đã thực hiện nó cho bạn?" Đây là vấn đề chúng ta phải đối mặt hàng ngày - phải tạo ra một giải pháp cho một vấn đề đã được giải quyết. –

+0

PropertyUtils.getProperty (đối tượng, propertyName); sử dụng sự phản chiếu dưới mui xe mà sẽ có thêm chi phí hiệu suất. Vì thế tôi sẽ sử dụng phương pháp tiếp cận của Jon Skeet. –

+4

Điều duy nhất tôi không thích về điều này là thiếu an toàn kiểu - nếu trường 'viewValue.id' được đổi tên, mã sẽ vẫn biên dịch nhưng không thành công khi chạy. –

24

Sử dụng bộ sưu tập google. Ví dụ:

Function<ViewValue, Long> transform = new Function<ViewValue, Long>() { 
     @Override 
     public Long apply(ViewValue from) { 
      return from.getId(); 
     } 
    }; 
    List<ViewValue> list = Lists.newArrayList(); 
    List<Long> idsList = Lists.transform(list, transform); 

UPDATE:

On Java 8 bạn không cần ổi. Bạn có thể:

import com.example.ViewValue; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.function.Function; 
import java.util.stream.Collectors; 

Function<ViewValue, Long> transform = ViewValue::getId; 
List<ViewValue> source = new ArrayList<>(); 
List<Long> result = source.stream().map(transform).collect(Collectors.toList()); 

Hoặc chỉ:

List<ViewValue> source= new ArrayList<>(); 
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList()); 

cập nhật tiếp theo (Người cuối cùng sau khi Javaslang để Vavr thay đổi tên):

Hiện nay nó có giá trị đề cập về các giải pháp với Thư viện Javaslang ( http://www.javaslang.io/) Thư viện Vavr (http://www.vavr.io/). Giả sử rằng chúng ta có danh sách của chúng tôi với các đối tượng chính hãng:

List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2)); 

Chúng ta có thể thực hiện chuyển đổi với Danh sách lớp từ thư viện Javaslang (trên dài chạy thu thập là không thuận tiện):

List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList(); 

Nhưng bạn sẽ xem sức mạnh chỉ với các danh sách của Javaslang:

io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3)); 
io.vavr.collection.List<Long> res = source.map(ViewValue::getId); 

Tôi khuyến khích xem bộ sưu tập sẵn có và các loại mới trên thư viện đó (Tôi thích đặc biệt là kiểu Thử). Bạn sẽ tìm thấy tài liệu theo địa chỉ sau: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.

PS. Do Oracle và từ "Java" trong tên họ đã phải thay đổi tên thư viện từ javaslang thành cái gì khác. Họ đã quyết định Vavr.

14

Chúng ta có thể làm điều đó trong một dòng mã sử dụng java 8

List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList()); 

Để biết thêm thông: Java 8 - Streams

+0

May mắn thay chúng ta đang nhận được những điều đơn giản như chúng ta đang phát triển :) –

+2

Ok hãy để tôi nói thẳng: Tôi sẽ thêm ý kiến, rằng điều này được kích hoạt với Java8 mà không có sẵn tại thời điểm viết. Câu trả lời thực sự là một bước tiến lớn đối với những gì đã được viết cách đây 7 năm. – Alex

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