Có nên đặt Set Set có phương thức Get không?


22

Hãy có lớp C # này (nó sẽ gần giống với Java)

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}

   public override bool Equals(object obj) {
        var item = obj as MyClass;

        if (item == null || this.A == null || item.A == null)
        {
            return false;
        }
        return this.A.equals(item.A);
   }

   public override int GetHashCode() {
        return A != null ? A.GetHashCode() : 0;
   }
}

Như bạn có thể thấy, sự bình đẳng của hai trường hợp chỉ MyClassphụ thuộc vào A. Vì vậy, có thể có hai trường hợp bằng nhau, nhưng giữ một phần thông tin khác nhau trong Btài sản của họ .

Trong một thư viện bộ sưu tập tiêu chuẩn gồm nhiều ngôn ngữ (bao gồm cả C # và Java, tất nhiên) có một Set( HashSettrong C #), một bộ sưu tập, có thể chứa tối đa một mục từ mỗi bộ phiên bản bằng nhau.

Người ta có thể thêm các mục, loại bỏ các mục và kiểm tra xem bộ có chứa một mục không. Nhưng tại sao không thể có được một mặt hàng cụ thể từ bộ này?

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

//I can do this
if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) {
    //something
}

//But I cannot do this, because Get does not exist!!!
MyClass item = mset.Get(new MyClass {A = "Hello", B = "See you"});
Console.WriteLine(item.B); //should print Bye

Cách duy nhất để lấy lại mục của tôi là lặp lại toàn bộ bộ sưu tập và kiểm tra tất cả các mục để tìm sự bình đẳng. Tuy nhiên, điều này cần có O(n)thời gian thay vì O(1)!

Tôi chưa tìm thấy bất kỳ ngôn ngữ nào hỗ trợ có được từ một bộ cho đến nay. Tất cả các ngôn ngữ "phổ biến" mà tôi biết (Java, C #, Python, Scala, Haskell ...) dường như được thiết kế theo cùng một cách: bạn có thể thêm các mục, nhưng bạn không thể truy xuất chúng. Có bất kỳ lý do chính đáng tại sao tất cả các ngôn ngữ này không hỗ trợ một cái gì đó dễ dàng và rõ ràng hữu ích? Họ không thể sai được, phải không? Có ngôn ngữ nào hỗ trợ nó không? Có thể lấy lại một mục cụ thể từ một bộ là sai, nhưng tại sao?


Có một vài câu hỏi SO liên quan:

/programming/7283338/getting-an-element-from-a-set

/programming/7760364/how-to-retrieve-actual-item-from-hashsett


12
C ++ std::sethỗ trợ truy xuất đối tượng, vì vậy không phải tất cả các ngôn ngữ "thông thường" đều giống như bạn mô tả.
Phục hồi lại

17
Nếu bạn tuyên bố (và mã) rằng "sự bình đẳng của hai phiên bản MyClass chỉ phụ thuộc vào A" thì một trường hợp khác có cùng giá trị A và B khác nhau thực sự "trường hợp cụ thể đó", vì chính bạn đã xác định rằng chúng bằng nhau và sự khác biệt trong B không quan trọng; container được "cho phép" trả về thể hiện khác vì nó bằng nhau.
Peteris

7
Câu chuyện có thật: trong Java, nhiều Set<E>triển khai chỉ Map<E,Boolean>ở bên trong.
corsiKa

10
nói chuyện với người A : "Xin chào, bạn có thể mang Người A đến đây được không"
Brad Thomas

7
Điều này phá vỡ tính phản xạ ( a == bluôn luôn đúng) trong trường hợp this.A == null. Các if (item == null || this.A == null || item.A == null)thử nghiệm là "quá trớn" và kiểm tra nhiều, có thể để tạo ra nhân tạo "chất lượng cao" mã. Tôi thấy loại "kiểm tra quá mức" này và luôn luôn chính xác trong Đánh giá mã.
usr

Câu trả lời:


66

Vấn đề ở đây không phải là HashSetthiếu một Getphương thức, đó là mã của bạn không có ý nghĩa gì từ góc độ của HashSetloại.

GetPhương pháp đó có hiệu quả, "làm ơn cho tôi giá trị này, làm ơn", theo đó dân gian của .NET framework sẽ trả lời một cách hợp lý, "eh? Bạn đã có giá trị đó <confused face />".

Nếu bạn muốn lưu trữ các mục và sau đó truy xuất chúng dựa trên việc khớp với một giá trị hơi khác, thì hãy sử dụng Dictionary<String, MyClass>như bạn có thể làm:

var mset = new Dictionary<String, MyClass>();
mset.Add("Hello", new MyClass {A = "Hello", B = "Bye"});

var item = mset["Hello"];
Console.WriteLine(item.B); // will print Bye

Thông tin về rò rỉ bình đẳng từ lớp đóng gói. Nếu tôi muốn thay đổi tập các thuộc tính liên quan Equals, tôi sẽ phải thay đổi mã bên ngoài MyClass...

Vâng, nhưng đó là vì MyClasschạy điên cuồng với nguyên tắc ít ngạc nhiên nhất (Pola). Với chức năng bình đẳng được gói gọn, hoàn toàn hợp lý khi cho rằng mã sau đây là hợp lệ:

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) 
{
    // this code is unreachable.
}

Để ngăn chặn điều này, MyClasscần phải được ghi chép rõ ràng về hình thức bình đẳng kỳ lạ của nó. Đã làm điều đó, nó không còn được gói gọn và thay đổi cách hoạt động của sự bình đẳng đó sẽ phá vỡ nguyên tắc mở / đóng. Ergo, nó không nên thay đổi và do đó Dictionary<String, MyClass>là một giải pháp tốt cho yêu cầu kỳ quặc này.


2
@vojta, trong trường hợp đó, hãy sử dụng Dictionary<MyClass, MyClass>vì nó sẽ tìm nạp giá trị dựa trên khóa sử dụng MyClass.Equals.
David Arno

8
Tôi sẽ sử dụng một Dictionary<MyClass, MyClass>cung cấp với một thích hợp IEqualityComparer<MyClass>và kéo mối quan hệ tương đương ra khỏi MyClassTại sao MyClassphải biết về mối quan hệ này qua các trường hợp của nó?
Caleth

16
@vojta và bình luận ở đó: " meh. Ghi đè việc thực hiện bằng để các đối tượng không bằng nhau" bằng nhau "là vấn đề ở đây. Yêu cầu một phương pháp có nội dung" đưa tôi đối tượng giống hệt với đối tượng này ", và sau đó mong đợi một vật thể không giống hệt được trả lại có vẻ điên rồ và dễ gây ra sự cố bảo trì "được đặt ra. Đó thường là vấn đề với SO: những câu trả lời thiếu sót nghiêm trọng được ủng hộ bởi những người không nghĩ đến việc cấy ghép mong muốn sửa chữa nhanh mã bị hỏng của họ ...
David Arno

6
@DavidArno: loại không thể tránh khỏi mặc dù chúng tôi vẫn kiên trì sử dụng các ngôn ngữ phân biệt giữa bình đẳng và danh tính ;-) Nếu bạn muốn hợp thức hóa các đối tượng bằng nhau nhưng không giống nhau thì bạn cần một phương thức không "giống tôi" đối tượng với đối tượng này ", nhưng" hãy cho tôi đối tượng kinh điển tương đương với đối tượng này ". Bất cứ ai nghĩ rằng HashSet.Get trong các ngôn ngữ này nhất thiết có nghĩa là "hãy cho tôi đối tượng giống hệt nhau" đã bị lỗi nghiêm trọng.
Steve Jessop

4
Câu trả lời này có nhiều tuyên bố chăn như ...reasonable to assume.... Tất cả điều này có thể đúng trong 99% các trường hợp nhưng khả năng lấy lại một vật phẩm từ một bộ có thể trở nên tiện dụng. Mã thế giới thực không thể luôn luôn tuân thủ các nguyên tắc Pola vv. Ví dụ: nếu bạn đang lặp lại các chuỗi không phân biệt chữ hoa chữ thường, bạn có thể muốn lấy mục "chính". Dictionary<string, string>là một cách giải quyết, nhưng nó có giá hoàn hảo.
usr

24

Bạn đã có mục "trong" tập hợp - bạn đã chuyển nó làm khóa.

"Nhưng đó không phải là trường hợp mà tôi gọi là Thêm với" - Có, nhưng bạn đặc biệt tuyên bố rằng chúng bằng nhau.

A Setcũng là trường hợp đặc biệt của một Map| Dictionary, với khoảng trống là loại giá trị (cũng không xác định được các phương thức vô dụng, nhưng điều đó không quan trọng).

Cấu trúc dữ liệu mà bạn đang tìm kiếm là Dictionary<X, MyClass>nơi Xbằng cách nào đó lấy As ra khỏi MyClass.

Loại từ điển C # là tốt về vấn đề này, vì nó cho phép bạn cung cấp IEqualityComparer cho các khóa.

Đối với ví dụ đã cho, tôi sẽ có những điều sau đây:

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}
}

public class MyClassEquivalentAs : IEqualityComparer<MyClass>{
   public override bool Equals(MyClass left, MyClass right) {
        if (Object.ReferenceEquals(left, null) && Object.ReferenceEquals(right, null))
        {
            return true;
        }
        else if (Object.ReferenceEquals(left, null) || Object.ReferenceEquals(right, null))
        {
            return false;
        }
        return left.A == right.A;
   }

   public override int GetHashCode(MyClass obj) {
        return obj?.A != null ? obj.A.GetHashCode() : 0;
   }
}

Được sử dụng như vậy:

var mset = new Dictionary<MyClass, MyClass>(new MyClassEquivalentAs());
var bye = new MyClass {A = "Hello", B = "Bye"};
var seeyou = new MyClass {A = "Hello", B = "See you"};
mset.Add(bye);

if (mset.Contains(seeyou)) {
    //something
}

MyClass item = mset[seeyou];
Console.WriteLine(item.B); // prints Bye

Có một số tình huống có thể có lợi cho mã có đối tượng khớp với khóa, để thay thế nó bằng tham chiếu đến đối tượng được sử dụng làm khóa. Ví dụ: nếu nhiều chuỗi được biết là khớp với một chuỗi trong bộ sưu tập được băm, thay thế các tham chiếu đến tất cả các chuỗi đó bằng các tham chiếu đến chuỗi trong bộ sưu tập có thể là một chiến thắng về hiệu suất.
supercat

@supercat hôm nay đạt được với a Dictionary<String, String>.
MikeFHay

@MikeFHay: Vâng, nhưng có vẻ hơi không hợp lý khi phải lưu trữ mỗi tham chiếu chuỗi hai lần.
supercat

2
@supercat Nếu bạn có nghĩa là một chuỗi giống hệt nhau , đó chỉ là thực tập chuỗi. Sử dụng các công cụ tích hợp. Nếu bạn có nghĩa là một loại đại diện "chính tắc" nào đó (một loại không thể đạt được bằng cách sử dụng các kỹ thuật thay đổi trường hợp đơn giản, v.v.), thì âm thanh đó về cơ bản là bạn cần một chỉ mục (theo nghĩa DB sử dụng thuật ngữ này). Tôi không thấy vấn đề gì với việc lưu trữ từng "hình thức không chính tắc" như là một khóa để ánh xạ tới một hình thức chính tắc. (Tôi nghĩ điều này cũng áp dụng tương tự nếu dạng "chính tắc" không phải là một chuỗi.) Nếu đây không phải là điều bạn đang nói, thì bạn đã hoàn toàn mất tôi.
jpmc26

1
Tùy chỉnh ComparerDictionary<MyClass, MyClass>là một giải pháp thực dụng. Trong Java, điều tương tự có thể đạt được bằng TreeSethoặc TreeMapcộng với tùy chỉnh Comparator.
Markus Kull

19

Vấn đề của bạn là bạn có hai khái niệm trái ngược nhau về sự bình đẳng:

  • bình đẳng thực tế, trong đó tất cả các lĩnh vực đều bằng nhau
  • đặt bình đẳng thành viên, trong đó chỉ có A bằng

Nếu bạn sẽ sử dụng mối quan hệ bình đẳng thực tế trong tập hợp của mình, thì vấn đề truy xuất một mục cụ thể từ tập hợp không phát sinh - để kiểm tra xem một đối tượng có trong tập hợp không, bạn đã có đối tượng đó chưa. Do đó, không bao giờ cần phải lấy một thể hiện cụ thể từ một tập hợp, giả sử bạn đang sử dụng quan hệ đẳng thức chính xác.

Chúng ta cũng có thể lập luận rằng một tập hợp là một kiểu dữ liệu trừu tượng được xác định hoàn toàn bởi S contains xhoặc x is-element-of Squan hệ (hàm đặc trưng của tính năng). Nếu bạn muốn các hoạt động khác, bạn không thực sự tìm kiếm một bộ.

Điều xảy ra khá thường xuyên - nhưng điều không phải là một tập hợp - là chúng ta nhóm tất cả các đối tượng thành các lớp tương đương riêng biệt . Các đối tượng trong mỗi lớp hoặc tập hợp con như vậy chỉ tương đương, không bằng nhau. Chúng ta có thể đại diện cho mỗi lớp tương đương thông qua bất kỳ thành viên nào của tập hợp con đó và sau đó nó trở nên mong muốn để truy xuất phần tử đại diện đó. Đây sẽ là một ánh xạ từ lớp tương đương đến thành phần đại diện.

Trong C #, một từ điển có thể sử dụng một mối quan hệ bình đẳng rõ ràng, tôi nghĩ vậy. Mặt khác, mối quan hệ như vậy có thể được thực hiện bằng cách viết một lớp bao bọc nhanh. Mã giả:

// The type you actually want to store
class MyClass { ... }

// A equivalence class of MyClass objects,
// with regards to a particular equivalence relation.
// This relation is implemented in EquivalenceClass.Equals()
class EquivalenceClass {
  public MyClass instance { get; }
  public override bool Equals(object o) { ... } // compare instance.A
  public override int GetHashCode() { ... } // hash instance.A
  public static EquivalenceClass of(MyClass o) { return new EquivalenceClass { instance = o }; }
}

// The set-like object mapping equivalence classes
// to a particular representing element.
class EquivalenceHashSet {
  private Dictionary<EquivalenceClass, MyClass> dict = ...;
  public void Add(MyClass o) { dict.Add(EquivalenceClass.of(o), o)}
  public bool Contains(MyClass o) { return dict.Contains(EquivalenceClass.of(o)); }
  public MyClass Get(MyClass o) { return dict.Get(EquivalenceClass.of(o)); }
}

"lấy một thể hiện cụ thể từ một tập hợp" Tôi nghĩ rằng điều này sẽ truyền đạt ý nghĩa của bạn trực tiếp hơn nếu bạn thay đổi "thể hiện" thành "thành viên". Chỉ là một gợi ý nhỏ. =) +1
jpmc26

7

Nhưng tại sao không thể có được một mặt hàng cụ thể từ bộ này?

Bởi vì đó không phải là những gì thiết lập cho.

Hãy để tôi viết lại ví dụ.

"Tôi có một Hashset mà tôi muốn lưu trữ các đối tượng MyClass và tôi muốn có được chúng bằng cách sử dụng thuộc tính A bằng với thuộc tính A của đối tượng A".

Nếu thay thế "Hashset" bằng "Bộ sưu tập", "đối tượng" bằng "Giá trị" và "thuộc tính A" bằng "Khóa", câu sẽ trở thành:

"Tôi có Bộ sưu tập mà tôi muốn lưu trữ Giá trị MyClass và tôi muốn có được chúng bằng cách sử dụng Khóa bằng với Khóa của đối tượng".

Những gì đang được mô tả là một từ điển. Câu hỏi thực tế đang được hỏi là "Tại sao tôi không thể coi Hashset là một từ điển?"

Câu trả lời là chúng không được sử dụng cho cùng một thứ. Lý do để sử dụng một bộ là để đảm bảo tính duy nhất của nội dung riêng lẻ, nếu không bạn chỉ có thể sử dụng Danh sách hoặc mảng. Hành vi được mô tả trong câu hỏi là từ điển dùng để làm gì. Tất cả các nhà thiết kế ngôn ngữ đã không làm hỏng. Họ không cung cấp phương thức get vì nếu bạn có đối tượng và nó nằm trong tập hợp, chúng tương đương nhau, có nghĩa là bạn sẽ "nhận" một đối tượng tương đương. Lập luận rằng Hashset nên được triển khai theo cách mà bạn có thể "lấy" các đối tượng không tương đương mà bạn đã xác định bằng nhau là không bắt đầu khi các ngôn ngữ cung cấp các cấu trúc dữ liệu khác cho phép bạn làm điều đó.

Một lưu ý về OOP và bình luận / câu trả lời bình đẳng. Bạn có thể có khóa của ánh xạ là thuộc tính / thành viên của giá trị được lưu trữ trong Từ điển. Ví dụ: có Guid là khóa và cũng là thuộc tính được sử dụng cho phương thức bằng là hoàn toàn hợp lý. Điều không hợp lý là có các giá trị khác nhau cho phần còn lại của các thuộc tính. Tôi thấy rằng nếu tôi đang đi theo hướng đó, có lẽ tôi cần phải suy nghĩ lại về cấu trúc lớp học của mình.


6

Ngay khi bạn ghi đè bằng, bạn sẽ ghi đè mã băm tốt hơn. Ngay sau khi bạn thực hiện điều này, "ví dụ" của bạn sẽ không bao giờ thay đổi trạng thái nội bộ nữa.

Nếu bạn không ghi đè bằng và mã băm, nhận dạng đối tượng VM được sử dụng để xác định đẳng thức. Nếu bạn đặt đối tượng này vào Tập hợp, bạn có thể tìm lại được.

Thay đổi giá trị của một đối tượng được sử dụng để xác định sự bằng nhau sẽ dẫn đến tính không thể truy cập của đối tượng này trong các cấu trúc dựa trên hàm băm.

Vì vậy, một Setter trên A là nguy hiểm.

Bây giờ bạn không có B mà không tham gia bình đẳng. Vấn đề ở đây là về mặt ngữ nghĩa không phải là kỹ thuật. Bởi vì kỹ thuật thay đổi B là trung tính với thực tế của bình đẳng. Về mặt ngữ nghĩa B phải là một cái gì đó giống như một lá cờ "phiên bản".

Điểm mấu chốt là:

Nếu bạn có hai đối tượng bằng A nhưng không phải B, bạn có một giả định rằng một trong những đối tượng này mới hơn đối tượng kia. Nếu B không có thông tin phiên bản thì giả định này bị ẩn trong thuật toán của bạn KHI bạn quyết định "ghi đè / cập nhật" đối tượng này trong Tập hợp. Vị trí mã nguồn này nơi điều này xảy ra có thể không rõ ràng vì vậy nhà phát triển sẽ gặp khó khăn trong việc xác định mối quan hệ giữa đối tượng X và đối tượng Y khác với X trong B.

Nếu B có thông tin phiên bản, bạn sẽ đưa ra giả định rằng trước đây chỉ có thể lấy được hoàn toàn từ mã. Bây giờ bạn có thể thấy, đối tượng Y đó là phiên bản mới hơn của X.

Hãy nghĩ về bản thân bạn: Danh tính của bạn vẫn là toàn bộ cuộc sống của bạn, có thể một số tính chất thay đổi (ví dụ như màu tóc của bạn ;-)). Chắc chắn bạn có thể cho rằng nếu bạn có hai bức ảnh, một với tóc nâu một với tóc xám, bạn có thể trẻ hơn trên ảnh với tóc nâu. Nhưng có lẽ bạn đã nhuộm tóc? Vấn đề là: BẠN có thể biết rằng bạn đã nhuộm tóc. Có thể khác? Để đặt điều này vào một bối cảnh hợp lệ, bạn phải giới thiệu tuổi tài sản (phiên bản). Sau đó, bạn là bạn rõ ràng về ngữ nghĩa và không tôn giáo.

Để tránh thao tác ẩn "thay thế cũ bằng đối tượng mới", Bộ không nên có Phương thức get. Nếu bạn muốn một hành vi như thế này, bạn phải làm cho nó rõ ràng bằng cách loại bỏ đối tượng cũ và thêm đối tượng mới.

BTW: Điều đó có nghĩa là gì nếu bạn vượt qua trong một đối tượng bằng với đối tượng bạn muốn nhận? Điều đó không có ý nghĩa. Giữ ngữ nghĩa của bạn sạch sẽ và không làm điều này mặc dù về mặt kỹ thuật sẽ không có ai cản trở bạn.


7
"Ngay khi bạn ghi đè bằng với bạn, bạn nên ghi đè mã băm tốt hơn. Ngay sau khi bạn thực hiện xong," ví dụ "của bạn sẽ không bao giờ thay đổi trạng thái nội bộ nữa." Tuyên bố đó có giá trị +100, ngay tại đó.
David Arno

+1 để chỉ ra sự nguy hiểm của sự bình đẳng và mã băm tùy thuộc vào trạng thái có thể thay đổi
Hulk

3

Cụ thể trong Java, HashSetban đầu được triển khai bằng cách sử dụng HashMapvà chỉ cần bỏ qua giá trị. Vì vậy, thiết kế ban đầu không lường trước được bất kỳ lợi thế nào trong việc cung cấp phương thức get HashSet. Nếu bạn muốn lưu trữ và truy xuất một giá trị chính tắc giữa các đối tượng khác nhau bằng nhau, thì bạn chỉ cần sử dụng một HashMapchính mình.

Tôi đã không cập nhật các chi tiết triển khai như vậy, vì vậy tôi không thể nói liệu lý do này có còn áp dụng đầy đủ trong Java hay không, chứ đừng nói đến C #, v.v. HashSet được thực hiện lại để sử dụng ít bộ nhớ hơn HashMap, trong mọi trường hợp sẽ là một thay đổi đột phá để thêm một phương thức mới vào Setgiao diện. Vì vậy, đó là khá nhiều đau đớn cho một lợi ích không phải ai cũng thấy là có giá trị.


Chà, trong Java, thể cung cấp một defaultthực hiện để thực hiện điều này theo cách không phá vỡ. Nó chỉ không có vẻ là một thay đổi cực kỳ hữu ích.
Hulk

@ Hulk: Tôi có thể sai, nhưng tôi nghĩ rằng bất kỳ triển khai mặc định nào cũng sẽ không hiệu quả, vì như người hỏi nói, "Cách duy nhất để lấy lại mặt hàng của tôi là lặp đi lặp lại toàn bộ bộ sưu tập và kiểm tra tất cả các mục cho sự bình đẳng". Rất tốt, bạn có thể làm điều đó theo cách tương thích ngược, nhưng thêm một hình ảnh xác thực mà hàm get kết quả chỉ đảm bảo chạy trong O(n)so sánh ngay cả khi hàm băm đang phân phối tốt. Sau đó, việc triển khai Setghi đè thực hiện mặc định trong giao diện, bao gồm HashSet, có thể mang lại sự đảm bảo tốt hơn.
Steve Jessop

Đồng ý - Tôi không nghĩ nó sẽ là một ý tưởng tốt. Tuy nhiên, sẽ có các ưu tiên cho loại hành vi này - List.get (chỉ mục int) hoặc - để chọn một triển khai mặc định được thêm gần đây List.sort . Đảm bảo độ phức tạp tối đa được cung cấp bởi giao diện, nhưng một số triển khai có thể làm tốt hơn rất nhiều so với các giao diện khác.
Hulk

2

Có một ngôn ngữ chính mà tập hợp của bạn có tài sản bạn muốn.

Trong C ++, std::setlà một bộ được đặt hàng. Nó có một .findphương thức tìm kiếm phần tử dựa trên toán tử thứ tự <hoặc bool(T,T)hàm nhị phân mà bạn cung cấp. Bạn có thể sử dụng find để thực hiện thao tác get bạn muốn.

Trong thực tế, nếu bool(T,T)hàm bạn cung cấp có một cờ cụ thể trên nó ( is_transparent), bạn có thể truyền vào các đối tượng của một loại khác mà hàm đó bị quá tải. Điều đó có nghĩa là bạn không phải dán trường thứ hai dữ liệu "giả", chỉ cần đảm bảo thao tác đặt hàng bạn sử dụng có thể đặt hàng giữa các loại tra cứu và loại có chứa.

Điều này cho phép hiệu quả:

std::set< std::string, my_string_compare > strings;
strings.find( 7 );

Ở đâu my_string_compare hiểu cách đặt hàng số nguyên và chuỗi mà không cần chuyển đổi số nguyên thành chuỗi (với chi phí tiềm năng).

Đối với unordered_set(bộ băm của C ++), không có cờ trong suốt tương đương (chưa). Bạn phải vượt qua trong một Tđến một unordered_set<T>.findphương pháp. Nó có thể được thêm vào, nhưng băm yêu cầu== và có một phần mềm, không giống như các bộ được đặt hàng chỉ yêu cầu một thứ tự.

Mô hình chung là container sẽ thực hiện tra cứu, sau đó cung cấp cho bạn một "trình vòng lặp" cho phần tử đó trong vùng chứa. Tại thời điểm đó, bạn có thể lấy phần tử trong tập hợp hoặc xóa nó, v.v.

Nói tóm lại, không phải tất cả các thùng chứa tiêu chuẩn của ngôn ngữ đều có những sai sót mà bạn mô tả. Các thùng chứa dựa trên trình lặp của thư viện chuẩn C ++ thì không, và ít nhất một số trong các thùng chứa đã tồn tại trước bất kỳ ngôn ngữ nào khác mà bạn mô tả và khả năng thực hiện thậm chí còn hiệu quả hơn hơn so với cách bạn mô tả thậm chí đã được thêm vào. Không có gì sai với thiết kế của bạn, hoặc muốn hoạt động đó; các nhà thiết kế của Bộ bạn đang sử dụng đơn giản không cung cấp giao diện đó.

Các thùng chứa tiêu chuẩn C ++ được thiết kế để bao bọc sạch các hoạt động cấp thấp của mã C cuộn bằng tay tương đương, được thiết kế để phù hợp với cách bạn có thể viết nó một cách hiệu quả khi lắp ráp. Lặp đi lặp lại của nó là một trừu tượng của con trỏ kiểu C. Các ngôn ngữ bạn đề cập đều đã chuyển từ con trỏ thành một khái niệm, vì vậy chúng không sử dụng sự trừu tượng hóa vòng lặp.

Có thể việc C ++ không có lỗ hổng này là một tai nạn của thiết kế. Đường dẫn tập trung vào vòng lặp có nghĩa là để tương tác với một mục trong vùng chứa kết hợp, trước tiên bạn lấy một trình vòng lặp đến phần tử, sau đó bạn sử dụng trình vòng lặp đó để nói về mục nhập trong vùng chứa.

Chi phí là có các quy tắc vô hiệu hóa lặp mà bạn phải theo dõi và một số thao tác yêu cầu 2 bước thay vì một (làm cho mã máy khách trở nên ồn hơn). Lợi ích là tính trừu tượng mạnh mẽ cho phép sử dụng nâng cao hơn so với những gì các nhà thiết kế API đã nghĩ đến ban đầu.

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.