2008-09-18 31 views
27

Giả sử bạn có một bộ sưu tập vài trăm đối tượng trong bộ nhớ và bạn cần truy vấn Danh sách này để trả về các đối tượng khớp với một số SQL hoặc Tiêu chí như truy vấn. Ví dụ, bạn có thể có một danh sách các đối tượng xe hơi và bạn muốn trả lại tất cả các xe được sản xuất trong thập niên 1960, với một tấm giấy phép bắt đầu bằng AZ, được đặt theo tên của mẫu xe.Làm thế nào để bạn truy vấn các bộ sưu tập đối tượng trong Java (Tiêu chí/giống SQL)?

Tôi biết về JoSQL, có ai đã sử dụng hoặc có bất kỳ trải nghiệm nào với các giải pháp khác/homegrown không?

Trả lời

11

Tôi đã sử dụng Apache Commons JXPath trong một ứng dụng sản xuất. Nó cho phép bạn áp dụng các biểu thức XPath cho các đồ thị của các đối tượng trong Java.

+0

là phiên dịch xpath? –

+0

nó là một thông dịch biểu thức XPath –

1

Tôi sẽ sử dụng Trình so sánh mất nhiều năm và mẫu biển số giấy phép làm thông số đầu vào. Sau đó, chỉ cần lặp qua bộ sưu tập của bạn và sao chép các đối tượng phù hợp. Bạn có thể sẽ tạo ra một gói toàn bộ các Comparators tùy chỉnh với cách tiếp cận này.

+0

có thể xây dựng được không? Tôi hiểu cách tạo một Comparator tùy chỉnh và thực hiện phương thức so sánh để so sánh trên một số thuộc tính. Nhưng tôi không chắc chắn làm thế nào để thực hiện điều này với nhiều tham số đầu vào? – stian

2

Nếu bạn cần một đối sánh cụ thể, bạn có thể có lớp thực hiện Comparator, sau đó tạo một đối tượng độc lập với tất cả các trường băm được bao gồm và sử dụng nó để trả về chỉ mục của đối sánh. Khi bạn muốn tìm nhiều đối tượng (có khả năng) trong bộ sưu tập, bạn sẽ phải chuyển sang một thư viện như JoSQL (đã hoạt động tốt trong các trường hợp tầm thường mà tôi đã sử dụng nó). Nói chung, tôi có xu hướng nhúng Derby vào ngay cả các ứng dụng nhỏ của tôi, sử dụng chú thích Hibernate để định nghĩa các lớp mô hình của tôi và để thỏa thuận Hibernate với các lược đồ lưu vào bộ nhớ đệm để giữ mọi thứ nhanh chóng.

+0

Nhúng một cơ sở dữ liệu trong bộ nhớ như âm thanh Derby giống như một ý tưởng hay, đặc biệt vì Derby hiện là một phần của JDK. Việc giới thiệu Hibernate vào hỗn hợp sẽ là một chút quá mức cần thiết cho việc sử dụng của tôi. Tôi chỉ cần đi với SQL/JDBC tôi đoán. – stian

0

Tùy chọn Comparator không phải là xấu, đặc biệt nếu bạn sử dụng các lớp ẩn danh (để không tạo các lớp thừa trong dự án), nhưng cuối cùng khi bạn nhìn vào luồng so sánh, nó giống như lặp lại toàn bộ bộ sưu tập cho mình, xác định chính xác các điều kiện cho các hạng mục phù hợp:

if (Car car : cars) { 
    if (1959 < car.getYear() && 1970 > car.getYear() && 
      car.getLicense().startsWith("AZ")) { 
     result.add(car); 
    } 
} 

tiếp theo là phân loại ... đó có thể là một cơn đau ở mặt sau, nhưng may mắn là có lớp Collectionssort phương thức của nó, một trong số đó nhận được một Comparator ...

+0

Đây là cách tiếp cận tôi đang sử dụng bây giờ và nó nhanh chóng trở nên khó sử dụng khi các tiêu chí tăng lên. Nhưng nó có lẽ không sao cho ví dụ đơn giản. – stian

3

Tiếp tục chủ đề Comparator, bạn cũng có thể muốn xem API Google Collections. Cụ thể, họ có giao diện được gọi là Predicate, có vai trò tương tự như Comparator, trong đó giao diện đơn giản có thể được sử dụng bởi phương pháp lọc, chẳng hạn như Sets.filter. Chúng bao gồm toàn bộ các triển khai biến vị ngữ hỗn hợp, để thực hiện AND, ORs, v.v.

Tùy thuộc vào kích thước của tập dữ liệu của bạn, có thể sử dụng phương pháp này hơn là phương pháp cơ sở dữ liệu quan hệ bên ngoài hoặc SQL.

22

Lọc là một cách để thực hiện việc này, như được thảo luận trong các câu trả lời khác.

Lọc không thể mở rộng được. Trên bề mặt thời gian phức tạp sẽ xuất hiện là O (n) (nghĩa là đã không thể mở rộng nếu số đối tượng trong bộ sưu tập sẽ phát triển), nhưng thực tế vì một hoặc nhiều hơn kiểm tra cần phải được áp dụng cho từng đối tượng tùy thuộc vào truy vấn, độ phức tạp thời gian chính xác hơn là O (nt) trong đó t là số lần kiểm tra áp dụng cho từng đối tượng.

Vì vậy, hiệu suất sẽ giảm dần khi các đối tượng bổ sung được thêm vào bộ sưu tập, và/hoặc khi số lượng kiểm tra trong truy vấn tăng lên.

Có một cách khác để thực hiện việc này, sử dụng lập chỉ mục và đặt lý thuyết.

Một cách tiếp cận là để build chỉ số trên lĩnh vực trong các đối tượng được lưu trữ trong bộ sưu tập và mà bạn sau đó sẽ kiểm tra trong truy vấn của bạn.

Giả sử bạn có một bộ sưu tập gồm Car đối tượng và mọi đối tượng Car đều có một trường color. Giả sử truy vấn của bạn tương đương với "SELECT * FROM cars WHERE Car.color = 'blue'". Bạn có thể xây dựng một chỉ mục trên Car.color, mà về cơ bản sẽ trông như thế này:

'blue' -> {Car{name=blue_car_1, color='blue'}, Car{name=blue_car_2, color='blue'}} 
'red' -> {Car{name=red_car_1, color='red'}, Car{name=red_car_2, color='red'}} 

Sau đó được đưa ra một truy vấn WHERE Car.color = 'blue', tập xe ô tô màu xanh có thể được lấy ra trong thời gian O() thời gian phức tạp. Nếu có các thử nghiệm bổ sung trong truy vấn của bạn, bạn có thể kiểm tra từng chiếc xe trong số ứng cử viên được đặt để kiểm tra xem nó có khớp với các thử nghiệm còn lại trong truy vấn của bạn không. Vì tập hợp ứng viên có thể nhỏ hơn đáng kể so với toàn bộ bộ sưu tập, độ phức tạp thời gian là nhỏ hơn O (n) (theo nghĩa kỹ thuật, xem các nhận xét bên dưới). Hiệu suất không làm giảm càng nhiều, khi các đối tượng bổ sung được thêm vào bộ sưu tập. Nhưng điều này vẫn chưa hoàn hảo, hãy đọc tiếp.

Cách tiếp cận khác, là những gì tôi sẽ gọi là chỉ mục truy vấn đứng. Để giải thích: với phép lặp và lọc thông thường, bộ sưu tập được lặp lại và mọi đối tượng được kiểm tra xem nó có phù hợp với truy vấn hay không. Vì vậy, lọc giống như chạy một truy vấn trên một bộ sưu tập. Một chỉ mục truy vấn đứng sẽ là một cách khác xung quanh, nơi mà bộ sưu tập thay vì chạy trên truy vấn, nhưng chỉ một lần cho mỗi đối tượng trong bộ sưu tập, mặc dù bộ sưu tập có thể được truy vấn bất kỳ số lần nào.

Một đứng chỉ số truy vấn sẽ như thế nào đăng ký một truy vấn với một số loại bộ sưu tập thông minh, như vậy là đối tượng được thêm vào và lấy ra từ bộ sưu tập, bộ sưu tập sẽ tự động kiểm tra từng đối tượng chống lại tất cả các đứng truy vấn đã được đăng ký với nó. Nếu một đối tượng khớp với một truy vấn đứng thì bộ sưu tập có thể thêm/xóa nó vào/từ một bộ chuyên dụng để lưu trữ các đối tượng khớp với truy vấn đó. Sau đó, các đối tượng khớp với bất kỳ truy vấn đã đăng ký nào có thể được truy xuất trong độ phức tạp thời gian O().

Thông tin bên trên được lấy từ CQEngine (Collection Query Engine). Về cơ bản, đây là một công cụ truy vấn NoSQL để truy xuất các đối tượng từ các bộ sưu tập Java bằng cách sử dụng các truy vấn giống SQL, mà không cần phải lặp lại thông qua bộ sưu tập. Nó được xây dựng xung quanh các ý tưởng trên, cộng thêm một số ý tưởng khác. Disclaimer: Tôi là tác giả. Đó là mã nguồn mở và trong maven trung tâm. Nếu bạn thấy hữu ích, vui lòng upvote câu trả lời này!

+0

Câu trả lời hay, nhưng bạn nên chỉnh sửa câu sau: "Vì tập hợp ứng viên có thể nhỏ hơn đáng kể so với toàn bộ bộ sưu tập, độ phức tạp thời gian nhỏ hơn O (n)". Điều này là không chính xác. Giả sử bạn có 5 màu khác nhau. Sau đó, kích thước của bộ ứng cử viên là trung bình 0,2n. Điều này dẫn đến O (0.2n) và O (0.2n) = O (n), xem http://en.wikipedia.org/wiki/Big_O_notation#Multiplication_by_a_constant. Khả năng mở rộng chỉ cải thiện, nếu số lượng * giá trị * khác nhau tăng đáng kể (ví dụ: bạn nhận được * đáng kể * nhiều màu sắc khác nhau khi tổng số phát triển). –

+0

Thú vị. Chức năng phức tạp thời gian của tôi được dự định là một công thức kiểu kỹ thuật thực tế. Nếu chúng ta tuân thủ quy tắc nghiêm ngặt Big O Notation cho "nhân bởi một vô hướng" trên wikipedia, và vì vậy thay đổi công thức từ O (0.2n) sang O (n), thì chúng ta sẽ loại bỏ thông tin về giá trị của phương pháp này so với các phương pháp khác, khi n npgall

5

vâng, tôi biết đó là một bài đăng cũ, nhưng công nghệ xuất hiện hàng ngày và câu trả lời sẽ thay đổi theo thời gian.

Tôi nghĩ rằng đây là một vấn đề tốt để giải quyết nó với LambdaJ. Bạn có thể tìm thấy nó ở đây: http://code.google.com/p/lambdaj/

Ở đây bạn có một ví dụ:

NHÌN CHO KHÁCH HÀNG ACTIVE // (phiên bản Iterable)

List<Customer> activeCustomers = new ArrayList<Customer>(); 
for (Customer customer : customers) { 
    if (customer.isActive()) { 
    activeCusomers.add(customer); 
    } 
} 

phiên bản LambdaJ

List<Customer> activeCustomers = select(customers, 
             having(on(Customer.class).isActive())); 

Tất nhiên, có loại beaut này y tác động đến hiệu suất (một chút ... trung bình 2 lần), nhưng bạn có thể tìm thấy một mã dễ đọc hơn không?

Nó có nhiều tính năng rất nhiều, ví dụ khác có thể được sắp xếp:

Sắp xếp lặp

List<Person> sortedByAgePersons = new ArrayList<Person>(persons); 
Collections.sort(sortedByAgePersons, new Comparator<Person>() { 
     public int compare(Person p1, Person p2) { 
      return Integer.valueOf(p1.getAge()).compareTo(p2.getAge()); 
     } 
}); 

Sắp xếp với lambda

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 
+0

Lamdaj có hoạt động trên Android không? – kylexy1357

+0

Tôi nghe nói rằng bạn có thể sử dụng nó nhưng có một số lỗi. Bạn nên đăng bài diễn đàn con lambdaj trước khi thử nó vì nó có thể là nguy hiểm. Nhân tiện, hãy nhớ rằng việc sử dụng các tác động lambdaj lên hiệu suất. Trong một số ví dụ, phải mất gấp 6 lần để đạt được nhiệm vụ của bạn, trong số những người khác 1,5 lần. –

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