2009-05-05 33 views
5

Nói rằng tôi có một java.util.List list và tôi muốn tạo ra một mới List bằng cách thêm một yếu tố e đến đầu list (ví dụ, tôi muốn khuyết điểmelist). Ví dụ, nếu listCons'ing một danh sách trong Java

[1,2,3,4] 

e5, sau đó cons(e,list) sẽ

[5,1,2,3,4] 

Đó là bật đèn xanh cho các yếu tố của listcons(e,list) để được chia sẻ, nhưng list không nên được sửa đổi.

Cách đơn giản nhất và/hoặc hiệu quả nhất để triển khai cons là gì? Không sao cho kết quả là không thể sửa đổi được. Việc sử dụng Thư viện Google Collections được cho phép.

Điều gì xảy ra nếu listcom.google.common.collect.ImmutableList?

+1

Đối với những người trong chúng ta không quen thuộc với Lisp, là những gì nhược điểm? –

+13

Tôi đã xác định hành vi và đưa ra một ví dụ. Nhiều hơn những gì bạn muốn? –

Trả lời

2

Bạn có thể sử dụng CompositeCollection không?

public Collection cons(Collection c1, Collection c2) 
{ 
    CompositeCollection cons = new CompositeCollection(); 
    cons.addComposited(c1); 
    cons.addComposited(c2); 
    return cons; 
} 

Điều này sẽ không bị ảnh hưởng bởi việc một trong các tham số có bất biến và vẫn được hỗ trợ bởi bộ sưu tập gốc c1 và c2 hay không.

Nếu bạn cần một List tôi có lẽ sẽ làm như sau:

public List cons(Collection c1, Collection c2) 
{ 
    ArrayList cons = new ArrayList(c1.size() + c2.size()); 
    cons.addAll(c1); 
    cons.addAll(c2); 
    return cons; 
} 
+3

Bộ sưu tập có phải là một Danh sách không? –

8
public static<T> List<T> cons(List<T> list, T t) { 
    ArrayList<T> result = new ArrayList<T>(list); 
    result.add(0, t); 
    return result; 
} 

Chỉnh sửa để đáp ứng với nhận xét: Kể từ khi câu hỏi yêu cầu "cách đơn giản nhất và/hoặc hiệu quả nhất để thực hiện khuyết điểm "Tôi đã đi với" đơn giản nhất ". Tôi sẽ không ngạc nhiên khi biết rằng có những cách hiệu quả hơn. Đưa phần tử vào trước danh sách là một cách tiếp cận hợp lệ khác và việc phân bổ kích thước chính xác ban đầu có thể cải thiện hiệu suất. Tối ưu hóa sớm là gốc rễ của mọi điều ác.

+1

Có ai biết cách thực hiện điều này trong thực tế không? Theo API Java: 'Chèn phần tử được chỉ định tại vị trí đã chỉ định trong danh sách này. Thay đổi phần tử hiện tại ở vị trí đó (nếu có) và bất kỳ phần tử tiếp theo nào ở bên phải (thêm một phần tử vào chỉ mục của chúng). 'Điều này có nghĩa là các yếu tố của' danh sách 'chỉ được lặp lại một lần? – AndreiM

+0

Woops. Quên đề cập đến rằng tôi đang trích dẫn tài liệu cho phương thức 'add (int index, Object o)' – AndreiM

+3

Có thể hiệu quả hơn khi tạo ArrayList với công suất yêu cầu, thêm phần tử đầu tiên và sau đó addAll list. Nhưng tôi nghĩ câu hỏi ban đầu muốn phần tử được thêm vào cuối danh sách, nơi nó không quan trọng lắm. –

7

Clojure cung cấp loại công cụ Lisp-y đó. Trong khi hầu hết mọi người nghĩ đến việc sử dụng Clojure cho ngôn ngữ (như tôi làm), các thư viện Clojure là tất cả các mã Java thực, và bạn có thể sử dụng các cấu trúc dữ liệu từ Java như một thư viện đặc biệt nếu bạn muốn. Bằng cách đó, bạn sẽ có được khả năng để làm nhược điểm và như vậy, và bạn có được sự bất biến mà Clojure sử dụng. Các đường dẫn dữ liệu Clojure cũng triển khai các kiểu Java tương đương.

Chỉ cần suy nghĩ theo một hướng khác.

+3

Một ý tưởng có thời gian chắc chắn sẽ đến. STM thực tế có thể là đóng góp lâu dài của Clojure cho môi trường sống của JVM. – alphazero

2

Tôi sẽ ném 2 xu vào, sau đó xem có ai đến với bất kỳ thứ gì thanh lịch hơn không. Trong trường hợp tổng quát:

<E> List<E> cons(E e, List<E> list) { 
    List<E> res = Lists.newArrayListWithCapacity(list.size() + 1); 
    res.add(e); 
    res.addAll(list); 
    return res; 
} 

Với một ImmutableList (không biết hiệu quả như thế nào đây là):

<E> ImmutableList<E> cons(E e, ImmutableList<E> list) { 
    return ImmutableList.<E>builder() 
         .add(e) 
         .addAll(list) 
         .build(); 
} 
+1

Có phải 'Lists.newArrayListWithExpectedSize' thực sự dễ gõ hơn' new ArrayList '? –

+0

Tôi đoán, đó là về việc tránh các hoạt động thay đổi kích thước có thể. Nhưng bạn có thể sử dụng http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html#ArrayList(int). Mã trên, tuy nhiên, là đối phó với 'kích thước dự kiến'. Không biết sự khác biệt giữa việc khởi tạo với công suất 'đã biết' là gì. – msi

+0

@mmyers: Trong trường hợp này, không, nhưng tôi duy trì quy ước sử dụng các phương thức tĩnh trong Danh sách để khởi tạo danh sách của tôi bất cứ khi nào có thể. Nó thường là một chiến thắng. –

2

Chắc chắn một LinkedList sẽ là cách hiệu quả nhất để chèn một mục ở đầu một danh sách?

Chỉ cần sử dụng lớp LinkedList mà đi kèm với Java

+0

Bạn giả định rằng chi phí sao chép danh sách gốc vào một LinkedList là không đáng kể. –

+0

Bạn không chỉ định triển khai Danh sách. Tôi đã sử dụng LinkedList vào một số trường hợp mà chi phí ban đầu của việc tạo ra không phải là một vấn đề nhưng chèn tiếp theo * là * một vấn đề. – Fortyrunner

3

Bởi vì bạn đề cập đến cons chức năng, tôi sẽ cho rằng bạn đang tiếp cận vấn đề này với các mô hình khái niệm của danh sách liên kết gồm cons tế bào. Cụ thể, tôi giả định rằng bạn đang nghĩ đến mỗi danh sách có một số car (phần tử đầu tiên) và một số cdr (danh sách con chứa tất cả các phần tử sau).

Java hỗ trợ danh sách được liên kết dưới dạng java.util.LinkedList. Đây là tốt cho traversal tuyến tính và có thể có các yếu tố chèn rất hiệu quả. Đây là hầu hết tương tự như các danh sách liên kết tôi đã đề cập ở trên.

Java cũng cung cấp java.util.ArrayList. Danh sách loại này tốt cho truy cập ngẫu nhiên, nhưng có thể chậm khi chèn các phần tử. Trong thực tế, chúng chậm nhất khi chèn một phần tử vào đầu danh sách. Bởi vì ArrayList s được triển khai dưới dạng mảng đằng sau hậu trường, mọi phần tử phải được sao chép một vị trí chuyển tiếp trong danh sách để tạo chỗ cho phần tử đầu tiên mới. Bây giờ, nếu bạn cũng cần một mảng lớn hơn, thì ArrayList sẽ cấp phát một mảng mới, sao chép tất cả các phần tử trên, v.v.

(của ImmutableList Google được trích dẫn là "truy cập ngẫu nhiên", vì vậy nó có khả năng tương tự như sau.)

Nếu bạn có kế hoạch để sử dụng phương pháp cons của bạn thường xuyên, tôi khuyên bạn nên sử dụng nó với danh sách liên kết. Hoạt động của cons về bản chất là thêm phần tử vào đầu danh sách. Điều này có ý nghĩa rất ít với các cấu trúc tuyến tính như mảng. Tôi khuyên bạn không nên sử dụng danh sách mảng vì lý do này: rằng chúng sai về mặt khái niệm cho công việc.

Đối với rất kén chọn: vì một danh sách mới được trả mỗi lần cons được gọi, sao chép phải xảy ra cho dù danh sách là một LinkedList hoặc một ArrayList. Tuy nhiên, toàn bộ hiệu trưởng của hoạt động cons là nó hoạt động trên danh sách được liên kết.

public <E> LinkedList<E> cons(E car, List<E> cdr) { 
    LinkedList<E> destination = new LinkedList<E>(cdr); 
    destination.addFirst(car); 
    return destination; 
} 

Lưu ý rằng đoạn mã trên được viết sau khi đọc câu trả lời ở trên, vì vậy tôi xin lỗi vì bất kỳ đạo văn ngẫu nhiên. Hãy cho tôi biết nếu bạn nhìn thấy nó và tôi sẽ thừa nhận đúng.

Giả sử bạn hài lòng khi trả lại LinkedList, bạn có thể sử dụng ImmutableList làm ví dụ cdr trong ví dụ này.

+0

Sử dụng destination.addFirst (ô tô) thay vì destination.add (0, ô tô). Nó sẽ không tạo ra sự khác biệt lớn về hiệu suất (có thể một số, mặc dù) nhưng tôi nghĩ nó rõ ràng hơn rất nhiều. –

+0

Đó là tốt hơn nhiều để xem xét - cảm ơn! Tôi đã nhìn vào giao diện java.util.List trong khi viết đoạn mã đó, vì vậy tôi đã bỏ qua nó. – Wesley

0

Một biến thể khác nếu bạn chỉ có thể lặp lại.

public static <E> List<E> cons(E e, Iterable<E> iter) { 
    List<E> list = new ArrayList<E>(); 
    list.add(e); 
    for(E e2: iter) list.add(e2); 
    return list; 
} 

public static <E> List<E> cons(Iterable<E>... iters) { 
    List<E> list = new ArrayList<E>(); 
    for(Iterable<E> iter: iters) for(E e1: iter1) list.add(e1); 
    return list; 
} 
4

Tôi tin rằng câu trả lời bạn đang thực sự tìm kiếm là thế này:

http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/List.html

Phương pháp này thậm chí còn được gọi là cons.

Tôi không có kinh nghiệm với thư viện này. Chỉ nghe nói về nó vào ngày khác. Tôi hy vọng nó tốt!

+0

Chức năng Java trông khá thú vị, nhưng tôi đã dựa khá nhiều vào Bộ sưu tập của Google và không ở trong thị trường cho một phụ thuộc thư viện mới :-( –

+3

Awwww ... mương mà Google crap sau đó. –

4

Giải pháp hiệu quả sẽ là tạo triển khai Danh sách của riêng bạn, ủy quyền nhẹ nhàng. Nếu không thể sửa đổi được, nó sẽ không quá rắc rối.Nếu được tạo thông qua một phương thức nhà máy, thì bạn thậm chí có thể sử dụng một trong hai cách triển khai cơ bản khác nhau tùy thuộc vào việc Danh sách đối số có thực hiện RandomAccess hay không.

Đối với trường hợp đơn giản nhất (chưa kiểm tra để xem liệu này biên dịch, không có kiểm tra sự tỉnh táo, vv):

class ConsList<E> extends AbstractList<E> 
{ 
    private final E first; 
    private final List<E> rest; 

    ConsList(E first, List<E> rest) 
    { 
     this.first = first; 
     this.rest = rest; 
    } 

    public int get(int index) 
    { 
     return (index == 0) ? first : rest.get(index - 1); 
    } 

    public int size() 
    { 
     return rest.size() + 1; 
    } 
} 

tôi chắc chắn rằng một vài trong số các phương pháp khác có thể được thực hiện hiệu quả hơn, và trường hợp tuần tự sẽ được phục vụ tốt hơn bằng cách mở rộng AbstractSequentialList thay thế.

+0

Và điều gì sẽ xảy ra đầu tiên Bạn có một nhược điểm thứ hai – Brian

+0

Tôi không thấy một vấn đề ở đó Mỗi một nhược điểm sẽ trả về một danh sách mới. một danh sách có "đầu tiên" là b và có "phần còn lại" là c. Danh sách được tạo * hoàn toàn * sử dụng khuyết điểm và EMPTY_LIST về cơ bản chỉ là danh sách được liên kết, nhưng phiên bản này có thể chuyển sang bất kỳ triển khai nào khác tại bất kỳ thời điểm nào nếu bạn không biết thực hiện phần còn lại. –

3

Điều này không được biết rõ, nhưng API trình biên dịch javac của Sun chứa một danh sách không thể thay đổi thực hiện trả về danh sách bất biến mới bằng các phương pháp như append()prepend(). Để sử dụng nó, bạn cần tools.jar trên classpath, và tuyên bố nhập khẩu này:

import com.sun.tools.javac.util.List; 

Sample Code:

final List<Integer> list = List.of(6, 7); 
System.out.println(list); 

final List<Integer> newList = 
    list.prepend(5)      // this is a new List 
     .prependList(List.of(1, 2, 3, 4)) // and another new List 
     .append(8)      // and another 
     .reverse();      // and yet another 
System.out.println(newList); 

System.out.println(list == newList); 

System.out.println(list.getClass()); 
System.out.println(newList.getClass()); 

Output:

6,7
8,7,6,5,4,3,2,1
false
lớp com.sun.tools.javac.util.List
lớp com.sun.tools.javac.util.List

Tôi biết đây là một API nội bộ và không nên được sử dụng trong sản xuất, nhưng nó rất thú vị (cuối cùng là một bộ sưu tập java cảm thấy đúng).

Lưu ý: Tất cả các phương pháp có thể thay đổi tiêu chuẩn từ CollectionList giao diện (như add()addAll()) ném UnsupportedOperationException, rõ ràng.

tham khảo:

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