2009-05-22 33 views
47

Vì Java không cho phép truyền các phương thức làm tham số, bạn sử dụng mẹo nào để thực hiện Python như hiểu danh sách trong Java?Đọc danh sách giống như Python trong Java

Tôi có một danh sách (ArrayList) của Chuỗi. Tôi cần phải chuyển đổi từng phần tử bằng cách sử dụng một hàm để tôi nhận được một danh sách khác. Tôi có một số chức năng mà lấy một String như đầu vào và trả về một String như đầu ra. Làm thế nào để tôi thực hiện một phương pháp chung mà có thể được đưa ra danh sách và chức năng như các tham số để tôi có thể nhận được một danh sách trở lại với mỗi phần tử được xử lý. Nó không thể theo nghĩa đen, nhưng tôi nên sử dụng thủ thuật nào?

Tùy chọn khác là viết một hàm mới cho mỗi hàm xử lý chuỗi nhỏ hơn mà chỉ đơn giản là vòng lặp trên toàn bộ danh sách, không tốt cho lắm.

+2

như một fyi, bạn có thể sử dụng Jython hoặc Scala để có được danh sách hiểu trên JVM – geowa4

+3

… hoặc Clojure! :) – Ashe

+0

SMH trong khi đọc tất cả các câu trả lời về điều này. Trong Python, bạn có thể dễ dàng viết một danh sách hiểu trong một dòng 40-60 ký tự. Tất cả các giải pháp được đề xuất ở đây là nhiều dòng, và hầu hết trong số chúng dài hơn một dòng mà nó sẽ lấy bằng Python. – ArtOfWarfare

Trả lời

33

Về cơ bản, bạn tạo một giao diện chức năng:

public interface Func<In, Out> { 
    public Out apply(In in); 
} 

và sau đó vượt qua trong một lớp con nặc danh cho phương pháp của bạn.

phương pháp của bạn hoặc có thể áp dụng các chức năng để mỗi yếu tố tại chỗ:

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) { 
    ListIterator<T> itr = list.listIterator(); 
    while (itr.hasNext()) { 
     T output = f.apply(itr.next()); 
     itr.set(output); 
    } 
} 
// ... 
List<String> myList = ...; 
applyToListInPlace(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

hoặc tạo mới List (về cơ bản tạo ra một ánh xạ từ danh sách đầu vào vào danh sách đầu ra):

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) { 
    List<Out> out = new ArrayList<Out>(in.size()); 
    for (In inObj : in) { 
     out.add(f.apply(inObj)); 
    } 
    return out; 
} 
// ... 
List<String> myList = ...; 
List<String> lowerCased = map(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

Loại nào phù hợp hơn tùy thuộc vào trường hợp sử dụng của bạn. Nếu danh sách của bạn cực kỳ lớn, giải pháp tại chỗ có thể là giải pháp khả thi duy nhất; nếu bạn muốn áp dụng nhiều chức năng khác nhau cho cùng một danh sách gốc để tạo nhiều danh sách phái sinh, bạn sẽ muốn có phiên bản map.

+1

Nhưng sau đó bạn yêu cầu tôi đặt mọi chức năng nhỏ trong một lớp khác vì chúng phải có tên chuẩn ('áp dụng' trong trường hợp của bạn). Đúng ? – euphoria83

+1

Không nhất thiết; lớp ẩn danh của bạn chỉ đơn giản có thể gọi hàm nhỏ bên trong apply(). Điều này gần như Java được đưa đến các con trỏ hàm mà không mạo hiểm vào các nguy hiểm của sự phản chiếu. –

+0

doToList đang phát minh lại bánh xe. Những gì bạn đã làm ở đây là một thiết kế kém của những gì thường được gọi là bản đồ. Giao diện thông thường là công khai tĩnh Danh sách bản đồ (Danh sách , Func f); Những gì nó làm là tạo ra một danh sách khác thay vì thay đổi một danh sách tại chỗ. Nếu bạn cần phải sửa đổi danh sách gốc mà không phá hủy tham chiếu, thì chỉ cần làm một .clear() theo sau là addAll(). Đừng kết hợp tất cả những gì trong một phương pháp. – Pyrolistical

16

Google Collections library có nhiều lớp để làm việc với bộ sưu tập và trình vòng lặp ở mức cao hơn nhiều so với hỗ trợ Java đơn giản và theo cách chức năng (bộ lọc, bản đồ, gấp, v.v.). Nó định nghĩa các giao diện Function và Predicate và các phương thức sử dụng chúng để xử lý các collection để bạn không phải làm như vậy. Nó cũng có các chức năng tiện lợi làm cho việc xử lý các generic Java ít khó khăn hơn.

Tôi cũng sử dụng Hamcrest ** để lọc các bộ sưu tập.

Hai thư viện rất dễ kết hợp với các lớp bộ điều hợp.


** Tuyên bố quan tâm: Tôi đồng sáng tác hamcrest

+11

Trong sự tò mò, tại sao nó được gọi là Hamcrest? Tôi vẫn không thể tìm ra nếu nó có vẻ ngon hay không. –

+12

Đó là một đảo chữ cái của "matchers". – Nat

5

Apache Commons CollectionsUtil.transform(Collection, Transformer) là một tùy chọn khác.

+0

Thật không may, nó không phải là chung chung. Trong trường hợp này tất cả điều đó có nghĩa là một số đúc thêm, nhưng nó có thể là một vấn đề trong các trường hợp khác. –

27

Trong Java 8 bạn có thể sử dụng tài liệu tham khảo phương pháp:

List<String> list = ...; 
list.replaceAll(String::toUpperCase); 

Hoặc, nếu bạn muốn tạo một danh sách mới dụ:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList()); 
+7

Câu hỏi là 7 tuổi và Java 8 không tồn tại sau đó. Đây sẽ là câu trả lời được chấp nhận ngay bây giờ;) – zpontikas

1

Tôi đang xây dựng dự án này để viết danh sách hiểu trong Java, bây giờ là bằng chứng về khái niệm trong https://github.com/farolfo/list-comprehension-in-java

Ví dụ

// { x | x E {1,2,3,4}^x is even } 
// gives {2,4} 

Predicate<Integer> even = x -> x % 2 == 0; 

List<Integer> evens = new ListComprehension<Integer>() 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// evens = {2,4}; 

Và nếu chúng ta muốn biến đổi biểu thức đầu ra một cách nào đó như

// { x * 2 | x E {1,2,3,4}^x is even } 
// gives {4,8} 

List<Integer> duplicated = new ListComprehension<Integer>() 
    .giveMeAll((Integer x) -> x * 2) 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// duplicated = {4,8} 
+1

Một phần của vẻ đẹp của việc hiểu danh sách Python là nó ngắn như thế nào. 6 dòng Java dài của bạn có thể được viết dưới dạng '[x * 2 cho x trong 1, 2, 3, 4 nếu x% 2 == 0]' ... 1 dòng gồm 41 ký tự. Bạn không chắc chắn bao nhiêu mã của bạn chỉ là khủng khiếp để đọc vì làm thế nào damn verbose Java là so với bao nhiêu là bởi vì thư viện của bạn không làm đủ mọi thứ một cách chính xác. – ArtOfWarfare

+0

Nó vẫn còn tốt hơn nhiều giải pháp khác ở đây, tôi thực sự thích điều này – rhbvkleef

0

Bạn có thể sử dụng lambdas định chức năng, như vậy:

class Comprehension<T> { 
    /** 
    *in: List int 
    *func: Function to do to each entry 
    */ 
    public List<T> comp(List<T> in, Function<T, T> func) { 
     List<T> out = new ArrayList<T>(); 
     for(T o: in) { 
      out.add(func.apply(o)); 
     } 
     return out; 
    } 
} 

việc sử dụng:

List<String> stuff = new ArrayList<String>(); 
stuff.add("a"); 
stuff.add("b"); 
stuff.add("c"); 
stuff.add("d"); 
stuff.add("cheese"); 
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String> 
    a.equals("a")? "1": 
      (a.equals("b")? "2": 
       (a.equals("c")? "3": 
        (a.equals("d")? "4": a 
    ))) 
}); 

sẽ trả lại:

["1", "2", "3", "4", "cheese"] 
Các vấn đề liên quan