2009-08-28 39 views

Trả lời

329

Không có sự khác biệt giữa các vật thể; bạn có một số HashMap<String, Object> trong cả hai trường hợp. Có sự khác biệt trong giao diện bạn phải đối tượng. Trong trường hợp đầu tiên, giao diện là HashMap<String, Object>, trong khi giao diện thứ hai là Map<String, Object>. Nhưng đối tượng cơ bản là như nhau.

Lợi thế khi sử dụng Map<String, Object> là bạn có thể thay đổi đối tượng bên dưới thành một loại bản đồ khác mà không vi phạm hợp đồng của bạn với bất kỳ mã nào đang sử dụng nó. Nếu bạn khai báo là HashMap<String, Object>, bạn phải thay đổi hợp đồng của mình nếu bạn muốn thay đổi triển khai cơ bản.


Ví dụ: Giả sử tôi viết lớp này:

class Foo { 
    private HashMap<String, Object> things; 
    private HashMap<String, Object> moreThings; 

    protected HashMap<String, Object> getThings() { 
     return this.things; 
    } 

    protected HashMap<String, Object> getMoreThings() { 
     return this.moreThings; 
    } 

    public Foo() { 
     this.things = new HashMap<String, Object>(); 
     this.moreThings = new HashMap<String, Object>(); 
    } 

    // ...more... 
} 

Lớp có một vài bản đồ nội bộ của STRING-> đối tượng mà nó chia sẻ (thông qua các phương pháp accessor) với các lớp con. Hãy nói rằng tôi viết nó với HashMap s để bắt đầu bởi vì tôi nghĩ rằng đó là cấu trúc thích hợp để sử dụng khi viết lớp.

Sau đó, Mary viết mã phân lớp nó. Cô ấy có một cái gì đó cô ấy cần phải làm gì với cả hai thingsmoreThings, tự nhiên đến nỗi cô đặt rằng trong một phương pháp phổ biến, và cô sử dụng cùng loại tôi sử dụng trên getThings/getMoreThings khi xác định các phương pháp của mình:

class SpecialFoo extends Foo { 
    private void doSomething(HashMap<String, Object> t) { 
     // ... 
    } 

    public void whatever() { 
     this.doSomething(this.getThings()); 
     this.doSomething(this.getMoreThings()); 
    } 

    // ...more... 
} 

Sau đó, tôi quyết định rằng thực sự, sẽ tốt hơn nếu tôi sử dụng TreeMap thay vì HashMap trong Foo. Tôi cập nhật Foo, thay đổi HashMap thành TreeMap. Bây giờ, SpecialFoo không biên dịch nữa, bởi vì tôi đã phá vỡ hợp đồng: Foo được sử dụng để nói nó được cung cấp HashMap s, nhưng bây giờ nó cung cấp TreeMaps thay thế. Vì vậy, chúng tôi phải sửa chữa SpecialFoo ngay bây giờ (và loại điều này có thể gợn sóng thông qua một codebase).

Trừ khi tôi đã có một lý do thực sự tốt để chia sẻ rằng việc thực hiện của tôi đã sử dụng một HashMap (và điều đó không xảy ra), những gì tôi nên làm là tuyên bố getThingsgetMoreThings như chỉ trở Map<String, Object> mà không bị bất kỳ cụ thể hơn đó.Trong thực tế, ngăn cản một lý do chính đáng để làm cái gì khác, ngay cả trong Foo tôi có lẽ nên tuyên bố thingsmoreThings như Map, không HashMap/TreeMap:

class Foo { 
    private Map<String, Object> things;    // <== Changed 
    private Map<String, Object> moreThings;   // <== Changed 

    protected Map<String, Object> getThings() {  // <== Changed 
     return this.things; 
    } 

    protected Map<String, Object> getMoreThings() { // <== Changed 
     return this.moreThings; 
    } 

    public Foo() { 
     this.things = new HashMap<String, Object>(); 
     this.moreThings = new HashMap<String, Object>(); 
    } 

    // ...more... 
} 

Lưu ý làm thế nào tôi hiện nay đang sử dụng Map<String, Object> ở khắp mọi nơi tôi có thể, chỉ được cụ thể khi tôi tạo các đối tượng thực tế.

Nếu tôi đã làm điều đó, sau đó Mary sẽ đã làm điều này:

class SpecialFoo extends Foo { 
    private void doSomething(Map<String, Object> t) { // <== Changed 
     // ... 
    } 

    public void whatever() { 
     this.doSomething(this.getThings()); 
     this.doSomething(this.getMoreThings()); 
    } 
} 

... và thay đổi Foo sẽ không làm SpecialFoo dừng biên dịch.

Giao diện (và các lớp cơ sở) cho phép chúng tôi tiết lộ chỉ với số lượng cần thiết, giữ tính linh hoạt của chúng tôi trong phạm vi để thực hiện thay đổi khi thích hợp. Nói chung, chúng tôi muốn có tài liệu tham khảo của chúng tôi càng cơ bản càng tốt. Nếu chúng tôi không cần biết đó là số HashMap, chỉ cần gọi số đó là Map.

Đây không phải là quy tắc mù, nhưng nói chung, mã hóa cho giao diện chung nhất sẽ kém giòn hơn so với mã hóa cho một cái gì đó cụ thể hơn. Nếu tôi nhớ điều đó, tôi sẽ không tạo ra một số Foo để đặt Mary cho thất bại với SpecialFoo. Nếu Mary đã nhớ rằng, sau đó mặc dù tôi đã nhầm Foo, cô ấy đã tuyên bố phương thức riêng của mình với Map thay vì HashMap và hợp đồng của tôi thay đổi Foo sẽ không ảnh hưởng đến mã của cô ấy.

Đôi khi bạn không thể làm điều đó, đôi khi bạn phải cụ thể. Nhưng trừ khi bạn có một lý do để được, err đối với giao diện ít cụ thể.

+0

do đó sự khác biệt duy nhất là khi tôi chuyển nó như một tham số ví dụ sau đó tôi cần tham chiếu một như Bản đồ và khác như HashMap nhưng chúng thực sự là cùng một loại đối tượng chính xác? – sepiroth

+0

Vâng, chúng là đối tượng chính xác, đó là về hợp đồng * mà bạn đang tạo bằng bất kỳ mã nào sử dụng nó.Tôi đã cập nhật câu trả lời một chút để làm rõ. –

+44

ah, do đó, sự khác biệt là nói chung, Bản đồ có một số phương pháp liên kết với nó. nhưng có nhiều cách khác nhau hoặc tạo bản đồ, chẳng hạn như HashMap và những cách khác nhau này cung cấp các phương thức duy nhất mà không phải tất cả các bản đồ đều có. Vì vậy, nếu tôi sử dụng Bản đồ, tôi chỉ có thể sử dụng các phương pháp Bản đồ, nhưng tôi có một HashMap bên dưới để bất kỳ lợi ích tốc độ, lợi ích tìm kiếm, vv của HashMap sẽ được nhìn thấy trong Bản đồ. Và nếu tôi sử dụng HashMap, tôi có thể sử dụng những phương thức cụ thể HashMap đó và nếu cuối cùng tôi cần phải thay đổi loại bản đồ thì sẽ có nhiều công việc hơn. – sepiroth

3

Bạn tạo các bản đồ tương tự.

Nhưng bạn có thể điền vào sự khác biệt khi sử dụng. Với trường hợp đầu tiên, bạn sẽ có thể sử dụng các phương thức HashMap đặc biệt (nhưng tôi không nhớ bất kỳ ai thực sự hữu ích), và bạn sẽ có thể chuyển nó thành tham số HashMap:

public void foo (HashMap<String, Object) { ... } 

... 

HashMap<String, Object> m1 = ...; 
Map<String, Object> m2 = ...; 

foo (m1); 
foo ((HashMap<String, Object>)m2); 
8

Trong ví dụ thứ hai, tham chiếu "bản đồ" là loại Map, là giao diện được triển khai bởi HashMap (và các loại Map) khác. Giao diện này là hợp đồng cho biết đối tượng ánh xạ khóa tới giá trị và hỗ trợ các hoạt động khác nhau (ví dụ: put, get). Nó nói không có gì về việc thực hiện của Map (trong trường hợp này là HashMap).

Cách tiếp cận thứ hai thường được ưu tiên vì bạn thường không muốn hiển thị việc triển khai bản đồ cụ thể cho các phương pháp sử dụng Map hoặc thông qua định nghĩa API.

1

Bản đồ là Giao diện và Hashmap là lớp thực hiện điều đó.

Vì vậy, trong việc thực hiện điều này, bạn tạo ra các đối tượng cùng

12

Theo ghi nhận của TJ Crowder và Adamski, một tài liệu tham khảo là một giao diện, khác với một thực hiện cụ thể của giao diện. Theo Joshua Block, bạn nên luôn cố gắng mã hóa các giao diện, để cho phép bạn xử lý tốt hơn các thay đổi đối với triển khai cơ bản - tức là nếu HashMap đột nhiên không lý tưởng cho giải pháp của bạn và bạn cần thay đổi việc triển khai bản đồ, bạn vẫn có thể sử dụng Bản đồ và thay đổi loại instantiation.

51

Map là giao diện HashMap thực hiện.Sự khác biệt là trong lần thực hiện thứ hai, tham chiếu đến HashMap sẽ chỉ cho phép sử dụng các hàm được định nghĩa trong giao diện Bản đồ, trong khi đầu tiên sẽ cho phép sử dụng bất kỳ chức năng công cộng nào trong HashMap (bao gồm giao diện Bản đồ).

Nó có lẽ sẽ có ý nghĩa hơn nếu bạn đọc Sun's interface tutorial

+0

Tôi giả sử: đầu tiên = HashMap map = new HashMap (); – OneWorld

+0

Nó tương tự như mức độ thường xuyên một danh sách được thực hiện như một ArrayList – Gerard

8

Bản đồ là tĩnh loại của bản đồ, trong khi HashMap là động loại của bản đồ. Điều này có nghĩa là trình biên dịch sẽ xử lý đối tượng bản đồ của bạn như là một loại Bản đồ, mặc dù trong thời gian chạy, nó có thể trỏ đến bất kỳ loại phụ nào của nó.

Thực hành lập trình này chống lại giao diện thay vì triển khai có lợi ích bổ sung cho việc linh hoạt còn lại: Ví dụ: bạn có thể thay thế kiểu động của bản đồ, miễn là nó là loại phụ của Bản đồ (ví dụ LinkedHashMap) và thay đổi hành vi của bản đồ khi đang bay. Một nguyên tắc chung là duy trì càng trừu tượng càng tốt ở cấp API: Nếu ví dụ một phương pháp bạn đang lập trình phải làm việc trên bản đồ, thì sẽ đủ để khai báo tham số dưới dạng Bản đồ thay vì chặt chẽ hơn (vì ít hơn trừu tượng) loại HashMap. Bằng cách đó, người tiêu dùng API của bạn có thể linh hoạt về loại triển khai Bản đồ mà họ muốn chuyển cho phương pháp của bạn.

17

tôi chỉ cần đi để làm điều này như một lời nhận xét về câu trả lời chấp nhận nhưng nó đã quá sôi nổi (Tôi ghét không có ngắt dòng)

ah, vì vậy sự khác biệt là trong chung, Bản đồ có một số phương thức nhất định được liên kết với nó. nhưng có các cách khác nhau hoặc tạo bản đồ, chẳng hạn dưới dạng HashMap và các cách khác nhau này cung cấp các phương thức duy nhất không phải tất cả các bản đồ đều có.

Chính xác - và bạn luôn muốn sử dụng giao diện chung nhất mà bạn có thể có. Xem xét ArrayList vs LinkedList. Sự khác biệt lớn trong cách bạn sử dụng chúng, nhưng nếu bạn sử dụng "Danh sách" bạn có thể chuyển đổi giữa chúng dễ dàng.

Thực tế, bạn có thể thay thế phía bên tay phải của bộ khởi tạo bằng một câu lệnh động hơn. làm thế nào về một cái gì đó như thế này:

List collection; 
if(keepSorted) 
    collection=new LinkedList(); 
else 
    collection=new ArrayList(); 

Bằng cách này nếu bạn đang đi để điền vào các bộ sưu tập với một loại chèn, bạn sẽ sử dụng một danh sách liên kết (một loại chèn vào một danh sách mảng là tội phạm.) Nhưng nếu bạn không cần phải giữ cho nó được sắp xếp và chỉ là phụ thêm, bạn sử dụng một ArrayList (hiệu quả hơn cho các hoạt động khác). Đây là một đoạn khá lớn ở đây bởi vì bộ sưu tập không phải là ví dụ tốt nhất, nhưng trong thiết kế OO một trong những khái niệm quan trọng nhất là sử dụng mặt tiền giao diện để truy cập các đối tượng khác nhau với cùng mã chính xác.

Sửa đáp lại bình luận:

Đối với bình luận bản đồ của bạn dưới đây, Có sử dụng "Bản đồ" giao diện hạn chế bạn chỉ có những phương pháp trừ khi bạn cast bộ sưu tập lại từ Map để HashMap (mà hoàn toàn đánh bại mục đích).

Thường thì những gì bạn sẽ làm là tạo đối tượng và điền vào bằng loại cụ thể (HashMap), trong một số phương thức "tạo" hoặc "khởi tạo", nhưng phương thức đó sẽ trả về "Bản đồ" t cần phải được thao tác như một HashMap nữa.

Nếu bạn đã từng phải truyền bằng cách này, có thể bạn đang sử dụng giao diện sai hoặc mã của bạn không được cấu trúc đủ tốt. Lưu ý rằng có một phần trong mã của bạn coi nó là một "HashMap" trong khi một phần khác coi nó là "Bản đồ", nhưng điều này sẽ chảy "xuống". để bạn không bao giờ đúc.

Cũng lưu ý khía cạnh bán gọn của các vai trò được chỉ định bởi giao diện. Một LinkedList tạo ra một stack hay hàng đợi tốt, ArrayList tạo ra một stack tốt nhưng một hàng đợi khủng khiếp (một lần nữa, một loại bỏ sẽ gây ra một sự thay đổi trong toàn bộ danh sách) để LinkedList thực hiện giao diện Queue, ArrayList thì không.

+0

nhưng trong ví dụ này, tôi chỉ nhận được các phương thức từ lớp danh sách chung, phải không? bất kể tôi có tạo cho nó một LinkedList() hay một ArrayList() không? nó chỉ là nếu tôi sử dụng sắp xếp chèn (mà tôi tưởng tượng phải là một phương pháp cho danh sách LinkedList và ArrayList được thừa hưởng) nó hoạt động cách nhanh hơn trên LinkedList? – sepiroth

+0

tôi đoán những gì tôi đang tìm là có hay không khi tôi nói Bản đồ m = new HashMap () Bản đồ m của tôi có thể sử dụng các phương pháp cụ thể để HashMap, hay không. Tôi nghĩ nó không thể? – sepiroth

+0

ah, chờ đã, không, Bản đồ m của tôi ở trên phải có các phương thức từ HashMap. – sepiroth

0

HashMap là một thực hiện Bản đồ do đó, nó khá giống nhau nhưng có "clone()" phương pháp như tôi nhìn thấy trong hướng dẫn tham khảo))

0
HashMap<String, Object> map1 = new HashMap<String, Object>(); 
Map<String, Object> map2 = new HashMap<String, Object>(); 

Trước hết Map là một giao diện nó có thực hiện khác nhau như - HashMap, TreeHashMap, LinkedHashMap vv Giao diện hoạt động như một lớp siêu cho lớp triển khai. Vì vậy, theo quy tắc của OOP, bất kỳ lớp cụ thể nào thực hiện Map cũng là một số Map. Điều đó có nghĩa là chúng tôi có thể chỉ định/đặt bất kỳ biến loại HashMap nào vào biến loại Map mà không có bất kỳ loại truyền nào.

Trong trường hợp này, chúng ta có thể gán map1-map2 mà không cần bất kỳ đúc hoặc bất kỳ thua của dữ liệu -

map2 = map1 
16

enter image description here

Bản đồ có triển khai sau,

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Tree Bản đồ Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

Giả sử bạn đã tạo ra một phương pháp (Nó chỉ spudo code).

public void HashMap getMap(){ 
    return map; 
} 

Giả sử bạn dự án yêu cầu đang thay đổi mỗi lần như sau,

  1. Phương pháp phải trả lại nội dung bản đồ - Cần trở HashMap.
  2. Phương thức phải trả lại khóa của bản đồ theo thứ tự chèn - Cần thay đổi loại trả lại HashMap thành LinkedHashMap.
  3. Phương thức phải trả về khóa bản đồ theo thứ tự được sắp xếp - Cần thay đổi loại trả lại LinkedHashMap thành TreeMap.

Nếu phương thức của bạn trả về các Lớp cụ thể thay vì giao diện Map, bạn phải thay đổi phương thức trả về getMap() mỗi lần.

Nhưng, Nếu bạn sử dụng polymorphism tính năng của java, Thay vì trả lại lớp học cụ thể sử dụng giao diện Map, Nó dẫn sử dụng lại mã và tác động ít hơn nếu có sự thay đổi yêu cầu.

2

Map là giao diện và HashMap là một lớp mà thực hiện Bản đồ Giao diện

0

Thêm vào đầu bình chọn câu trả lời và nhiều người trên nhấn mạnh sự "chung chung hơn, tốt hơn", tôi muốn đào hơn một chút.

Map là hợp đồng cấu trúc trong khi HashMap là triển khai cung cấp các phương pháp riêng để xử lý các vấn đề thực tế khác nhau: cách tính chỉ mục, dung lượng và cách tăng chỉ số, cách chèn, cách giữ chỉ mục duy nhất vv

chúng ta hãy nhìn vào mã nguồn:

trong Map chúng tôi có phương pháp containsKey(Object key):

boolean containsKey(Object key); 

Java Doc:

boolean java.util.Map.containsValue (Object value)

trả về true nếu bản đồ này đồ một hoặc nhiều phím với giá trị quy định. Chính thức hơn, trả về true nếu và chỉ khi bản đồ này chứa ít nhất một ánh xạ tới một giá trị v sao cho (value==null ? v==null : value.equals(v)). Hoạt động này có thể sẽ yêu cầu thời gian tuyến tính trong kích thước bản đồ cho hầu hết các triển khai của giao diện Bản đồ.

Tham số: giá trị

giá trị mà sự hiện diện trong bản đồ này là để betested

Returns: true

nếu bản đồ này đồ một hoặc nhiều phím để các quy định

valueThrows:

ClassCastException - nếu giá trị thuộc loại không phù hợp cho bản đồ này (tùy chọn)

NullPointerException - nếu giá trị quy định là null và bản đồ này không cho phép giá trị null (không bắt buộc)

Nó đòi hỏi việc triển khai của mình để thực hiện nó, nhưng "làm thế nào để" là sự tự do của mình, chỉ để đảm bảo nó trả về đúng.

Trong HashMap:

public boolean containsKey(Object key) { 
    return getNode(hash(key), key) != null; 
} 

Nó chỉ ra rằng HashMap sử dụng hashcode để kiểm tra xem bản đồ này chứa chìa khóa. Vì vậy, nó có lợi ích của thuật toán băm.

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