Bản đồ lồng nhau so với các phím kết hợp


8

trong dự án tôi hiện đang làm việc, chúng tôi có ba loại giá khác nhau tùy thuộc vào độ tuổi của người dùng (người lớn, trẻ em, v.v ...). Vì vậy, chúng tôi đã có trên DB một bảng trông như thế này:

PRICES
type     Amount
A         20
B         15
C         ..
D         ..

Lúc đầu, chúng tôi chỉ có 4 loại giá khác nhau, vì vậy trong mã, chúng tôi có một cái gì đó như thế này:

Map<String, BigDecimal> prices = new HashMap<String, BigDecimal>();

Trường hợp các phím là loại giá.

Gần đây, họ đã thêm một quy tắc kinh doanh mới có thêm 3 loại phụ cho mọi loại giá, vì vậy bây giờ chúng tôi có một cái gì đó như thế này:

PRICES
type   subtype  Amount
A          1      20
A          2      15
A          3      ..
B          1      ..
B          2      ..
...        ..     ..

Bạn nghĩ lựa chọn nào trong hai lựa chọn sau là tốt hơn và tại sao?

Bản đồ lồng nhau

Map<String, Map<String, BigDecimal>> prices;

Trong đó các khóa là loại giá và loại phụ:

prices.get(type).get(subtype);

Phím kết hợp

Bản đồ giống với bản gốc:

Map<String, BigDecimal> prices;

Và nối các khóa để lập chỉ mục giá khác nhau:

prices.get(type+"_"+subtype);

Đây là nhiều hơn một câu hỏi thiết kế, và không có đủ mã cho Đánh giá mã.
200_success

Một lưu ý nhỏ: khóa kết hợp được đề xuất của bạn có thể dẫn đến các vấn đề trên đường, ví dụ như cần '_' trong một loại giá. Thay vào đó hãy xem xét có một class PriceKey{ PriceType type; PriceSubtype subtype; }chìa khóa. Điều này sau đó có thể dễ dàng được mở rộng hơn nữa
Caleth

Câu trả lời:


8

Cả hai phím lồng nhau và kết hợp có vị trí của chúng. Bowmore đưa ra một đối số pro cho các khóa tổng hợp và một đối số con cho các bản đồ lồng nhau. Hãy để tôi cung cấp sự phản đối trung thành:

Các khóa bản đồ tổng hợp hoạt động tuyệt vời khi bạn đang tìm kiếm một mục cụ thể được biết đến.

Các bản đồ lồng nhau hoạt động tốt khi bạn cần nhanh chóng tìm thấy tất cả các biến thể, loại và kiểu con loại A. Ví dụ: chọn A (so với B, C, ...) có thể là bước đầu tiên trong cây quyết định. Khi người dùng hoặc thuật toán hoặc bất cứ điều gì chọn A, thì bạn chỉ cần biết về các kiểu con của A và B..Z hoặc B..ZZZZZ không còn quan trọng nữa.

Bây giờ bạn đang xử lý một cấu trúc tra cứu rất chặt chẽ và hiệu quả cho việc tìm kiếm phụ. Nếu bạn cố gắng làm điều đó với các phím tổng hợp, cuối cùng bạn sẽ thực hiện quét toàn bộ bảng [ (key, value) for (key, value) in prices.items() if key.startswith('A') ]. Đó không phải là một hoạt động hiệu quả, và sẽ chậm nếu bản đồ rộng lớn.

Bản đồ lồng nhau cũng hoạt động tốt khi số lượng cấp độ lồng có thể tăng lên. Cấu trúc vấn đề đã được mở rộng từ typeđến (type, subtype). Có bất kỳ cơ hội mà rev tiếp theo sẽ cần (type, subtype, variation)hay (type, subtype, version)không? Nếu vậy, một cách tiếp cận ánh xạ lồng nhau có thể được mở rộng rõ ràng. Tuy nhiên, đây là một lợi thế theo kiểu thứ hai, đặc biệt so với lợi thế "dễ dàng tìm kiếm" ở trên.


sự thật được nói, tìm kiếm gia tăng cho các khóa tổng hợp có thể tránh được 'quét bảng' bằng cách sử dụng Bản đồ sắp xếp.
Bowmore

Bạn có thể cải thiện các đặc điểm tìm kiếm cho các phím tổng hợp bằng cách sắp xếp bản đồ. Nhưng bạn không thể tránh tất cả các cách quét theo cách đó. Tìm khóa "RXR_1_a" trong một bảng hoặc sắp xếp ánh xạ 10.000 khóa, vẫn yêu cầu tìm chỉ mục bảng trong đó "RXR_ *" bắt đầu và kết thúc. Ngay cả với tìm kiếm nhị phân, đây không phải là thời gian bằng không. Nó cũng yêu cầu các key.startswith('RXR')bài kiểm tra tương tự tôi đã đề cập. Các bản đồ lồng nhau luôn là các phép toán O (1) và không yêu cầu chi phí sắp xếp trước bản đồ hoặc so sánh chuỗi.
Jonathan Eunice

Đúng vậy, tôi vừa nói rằng bạn có thể tránh được thao tác kiểu 'quét bảng'. Trong câu trả lời của tôi, tôi cũng chỉ ra rằng đối với loại tìm kiếm này, việc truy vấn DB trực tiếp sẽ dễ dàng hơn. Cả hai cách tiếp cận sẽ gặp rắc rối với việc tìm kiếm khóa một phần không bắt đầu ở trường đầu tiên và DB có thể làm giảm bớt điều này với các chỉ số bổ sung.
Bowmore

Một SortedMap có thể tránh một tìm kiếm tuyến tính đầy đủ, nhưng không phải là một tìm kiếm chi phí cao. Tôi không thấy làm thế nào để giải quyết các hoạt động trên cơ sở dữ liệu; điều đó có thể đơn giản hóa mã máy khách, có thể, nhưng với chi phí của các yêu cầu bên ngoài chậm và cấu trúc dữ liệu bổ sung (các chỉ số cần thiết để thực hiện thao tác thậm chí hiệu quả hợp lý). Một cấu trúc lồng nhau có một số nhược điểm, chắc chắn, nhưng nó khép kín và hiệu quả cao, có hoặc không có hỗ trợ cơ sở dữ liệu sao lưu.
Jonathan Eunice

Làm thế nào là một tìm kiếm nhị phân là một tìm kiếm chi phí cao?
Bowmore

6

Tránh các bản đồ lồng nhau. Chúng khó mở rộng hơn và dẫn đến mã rất dài dòng và khó đọc với tất cả các khai báo chung chung đang diễn ra.

Quan trọng hơn, Bản đồ trong Java có xu hướng tiêu thụ rất nhiều bộ nhớ. Việc tạo một Bản đồ với nhiều Bản đồ hơn sẽ chỉ làm vấn đề tiêu thụ bộ nhớ trở nên trầm trọng hơn.

Cuối cùng, một Bản đồ sử dụng các phím tổng hợp sẽ dễ dàng hơn để lý giải.

Sử dụng các phím tổng hợp sẽ làm cho cuộc sống của bạn dễ dàng hơn trong những trường hợp điển hình nhất, tuy nhiên một số điều sẽ khó hơn. Chẳng hạn, lấy tất cả giá cho một thành phần khóa cụ thể, nhưng bạn có nhiều khả năng truy vấn kết quả trực tiếp từ cơ sở dữ liệu hơn là chưng cất nó từ Bản đồ.


Tôi đồng ý với tuyên bố của bạn, tuy nhiên đôi khi các bản đồ lồng nhau là không thể tránh khỏi và trong kịch bản đó tôi đã không tìm thấy một cách tốt để thao tác chúng trong Java. Bất cứ khi nào chúng ta cần đọc / thao tác một cái gì đó chung chung trong Java, đặc biệt là các tệp JSON, bạn sẽ cần các bản đồ lồng nhau, trừ khi bạn muốn mã hóa cấu trúc của tệp trong ứng dụng của mình.
froginvasion

1

Điều này ít liên quan đến "việc triển khai nào là tốt nhất" và nhiều việc phải làm với "việc trừu tượng hóa nào tôi nên làm việc với".

Cả khóa tổng hợp và bản đồ của bản đồ đều có điểm mạnh và điểm yếu, tất cả đều nằm trong miền hiệu suất (nghĩa là sử dụng tốc độ / bộ nhớ). Chúng không khác nhau về chức năng của chúng: Cả hai đều lấy hai giá trị và trả về giá trị "đặt" trước đó.

Vì chúng tương đương về chức năng, điều đầu tiên bạn nên làm là trừu tượng hóa chúng. Đừng lo lắng về cái nào tốt hơn. Tạo giao diện DoubleKeyedMap với tất cả các phương thức bạn cần trên đó và sử dụng giao diện đó trong mã của bạn. Sau đó viết bất cứ điều gì bạn có thể viết nhanh nhất và tiếp tục.

CHỈ một khi bạn đã viết ứng dụng của mình và bạn thấy rằng việc triển khai khóa tổng hợp của bạn không lọc được khóa đầu tiên rất nhanh hoặc bản đồ bản đồ chiếm quá nhiều bộ nhớ nên bạn nên đi và tối ưu hóa.

Tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Không trừu tượng hóa là tồi tệ hơn.


Nói hay lắm. Không trừu tượng hóa là tồi tệ hơn! Tôi thích điều đó
Zack Bartel

0

Cả hai lựa chọn đều không tốt theo quan điểm của tôi. Nói gì nếu logic kinh doanh thay đổi một lần nữa để bạn có một kiểu con khác?

Những gì tôi đề nghị bạn làm là như sau:

  1. Sử dụng một khóa thay thế cho một bảng gọi Typebảng này sẽ trông như thế nàoType(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
  2. Trong Pricebảng của bạn, sử dụng khóa ngoại cho Typebảng ở trên để nó trông giống nhưPrice(INT type_id, INT price)
  3. Hãy chắc chắn rằng bạn KHÔNG hỗ trợ xóa một loại. Bạn chỉ cần đánh dấu một loại là inactivevì các tham chiếu lơ lửng sẽ khiến việc xóa đi đau đầu thực sự

Bây giờ, logic người dùng và doanh nghiệp có thể thêm bất kỳ kiểu kết hợp kiểu con nào vào lược đồ. Những gì họ cần làm chỉ đơn giản là tạo hàng mới trong Typebảng và thay đổi namevà / hoặc desctrong bảng cũ.

Trong trường hợp của bạn, người dùng sẽ đổi tên loại Ađể loại A_1, thêm một loại mới có tên gọi A_2và thiết lập giá mới cho A_2như15

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.