Lớp Java thực hiện Bản đồ và giữ thứ tự chèn?


463

Tôi đang tìm một lớp trong java có liên kết khóa-giá trị, nhưng không sử dụng băm. Đây là những gì tôi đang làm:

  1. Thêm giá trị cho a Hashtable.
  2. Nhận một iterator cho Hashtable.entrySet().
  3. Lặp lại thông qua tất cả các giá trị và:
    1. Nhận một Map.Entrycho các vòng lặp.
    2. Tạo một đối tượng kiểu Module(một lớp tùy chỉnh) dựa trên giá trị.
    3. Thêm lớp vào một JPanel.
  4. Hiển thị bảng điều khiển.

Vấn đề với điều này là tôi không có quyền kiểm soát thứ tự mà tôi lấy lại các giá trị, vì vậy tôi không thể hiển thị các giá trị theo thứ tự nhất định (không mã hóa thứ tự cứng).

Tôi sẽ sử dụng một ArrayListhoặc Vectorcho điều này, nhưng sau đó trong mã tôi cần lấy Moduleđối tượng cho một Khóa cụ thể, điều mà tôi không thể làm với một ArrayListhoặc Vector.

Có ai biết về một lớp Java mã nguồn mở / miễn phí sẽ làm điều này hay một cách để lấy các giá trị ra khỏi Hashtabledựa trên khi chúng được thêm vào không?

Cảm ơn!


1
Bạn không cần sử dụng entryset / map.entry. bạn có thể lặp lại các khóa và giá trị bằng cách sử dụng hashtable.keys làm bảng liệt kê hoặc bằng cách sử dụng hashtable.keyset.iterator.
John Gardner

5
Tôi đã tự do thay đổi tiêu đề, vì không sử dụng băm không thực sự là vấn đề, nhưng vẫn giữ trật tự chèn.
Joachim Sauer

Câu trả lời:


734

Tôi đề nghị một LinkedHashMaphoặc a TreeMap. A LinkedHashMapgiữ các khóa theo thứ tự chúng được chèn, trong khi a TreeMapđược sắp xếp thông qua một Comparatorhoặc thứ tự tự nhiên Comparablecủa các phần tử.

Vì nó không phải giữ các yếu tố được sắp xếp, LinkedHashMapnên sẽ nhanh hơn trong hầu hết các trường hợp; TreeMapO(log n)hiệu suất cho containsKey, get, put, và remove, theo Javadocs, trong khi LinkedHashMapO(1)cho mỗi.

Nếu API của bạn chỉ mong đợi một thứ tự sắp xếp có thể dự đoán được, trái ngược với thứ tự sắp xếp cụ thể, hãy xem xét sử dụng các giao diện mà hai lớp này triển khai NavigableMaphoặc SortedMap. Điều này sẽ cho phép bạn không rò rỉ các triển khai cụ thể vào API của mình và chuyển sang một trong các lớp cụ thể đó hoặc một triển khai hoàn toàn khác theo ý muốn sau đó.


2
Điều này sẽ không hoạt động đối với tôi bởi vì, theo javadocs, điều này chỉ cung cấp các giá trị theo thứ tự (thông qua lệnh gọi giá trị ()). Có cách nào để nhận các phiên bản Map.Entry không?
Cory Kendall

1
@CoryKendall: TreeMap không hoạt động? Nó được cho là được sắp xếp theo các khóa, không phải theo các giá trị.
Michael Myers

1
Sai lầm của tôi, tôi nghĩ rằng Bộ không được sắp xếp.
Cory Kendall

61
Xin lưu ý: Việc sắp xếp TreeMap dựa trên thứ tự tự nhiên của các khóa: "Bản đồ được sắp xếp theo thứ tự tự nhiên của các khóa". LinkedHashMap được sắp xếp thứ tự chèn bij. Sự khác biệt lớn!
Jop van Raaij

3
@AlexR: Điều đó chỉ đúng nếu LinkedHashMap được tạo bằng cách sử dụng hàm tạo đặc biệt được cung cấp cho mục đích đó. Theo mặc định, lặp là theo thứ tự chèn.
Michael Myers

22

LinkedHashMap sẽ trả về các phần tử theo thứ tự chúng được chèn vào bản đồ khi bạn lặp lại trên keyset (), entryset () hoặc value () của bản đồ.

Map<String, String> map = new LinkedHashMap<String, String>();

map.put("id", "1");
map.put("name", "rohan");
map.put("age", "26");

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
}

Điều này sẽ in các yếu tố theo thứ tự chúng được đưa vào bản đồ:

id = 1
name = rohan 
age = 26 

16

Nếu một bản đồ bất biến phù hợp với nhu cầu của bạn thì có một thư viện do google gọi là ổi (xem thêm câu hỏi về ổi )

Guava cung cấp một Bản đồ bất biến với thứ tự lặp đáng tin cậy do người dùng chỉ định. Bản đồ bất biến này có hiệu năng O (1) cho chứaKey, get. Rõ ràng đặt và loại bỏ không được hỗ trợ.

Các đối tượng ImmutableMap được xây dựng bằng cách sử dụng các phương thức tiện lợi tĩnh thanh lịch của ()copyOf () hoặc đối tượng Builder .


6

Bạn có thể duy trì Map(để tra cứu nhanh) và List(cho đơn hàng) nhưng LinkedHashMapcó thể là đơn giản nhất. Bạn cũng có thể thử một SortedMapví dụ TreeMap, trong đó có bất kỳ thứ tự nào bạn chỉ định.


1

Tôi không biết liệu nó có phải là nguồn mở hay không, nhưng sau một chút loay hoay, tôi đã tìm thấy cách triển khai Map này bằng ArrayList . Nó có vẻ là Java trước 1.5, vì vậy bạn có thể muốn khái quát hóa nó, điều này thật dễ dàng. Lưu ý rằng việc triển khai này có quyền truy cập O (N), nhưng điều này không thành vấn đề nếu bạn không thêm hàng trăm tiện ích vào JPanel, điều mà bạn không nên làm.



1

Bất cứ khi nào tôi cần duy trì trật tự tự nhiên của những thứ đã biết trước, tôi sử dụng EnumMap

các khóa sẽ là enum và bạn có thể chèn theo bất kỳ thứ tự nào bạn muốn nhưng khi bạn lặp lại nó sẽ lặp theo thứ tự enum (thứ tự tự nhiên).

Ngoài ra, khi sử dụng EnumMap, sẽ không có va chạm nào có thể hiệu quả hơn.

Tôi thực sự thấy rằng việc sử dụng enumMap làm cho mã dễ đọc. Đây là một ví dụ


1

Bạn có thể sử dụng LinkedHashMap theo thứ tự chèn chính trong Bản đồ

Các điểm quan trọng về lớp Java LinkedHashMap là:

  1. Nó chứa các yếu tố onlyunique.
  2. LinkedHashMap chứa các giá trị dựa trên khóa 3. Nó có thể có một khóa null và nhiều giá trị null. 4. Nó giống như HashMap thay vì duy trì thứ tự chèn

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> 

Nhưng nếu bạn muốn sắp xếp các giá trị trong bản đồ bằng cách sử dụng đối tượng do Người dùng xác định hoặc bất kỳ khóa kiểu dữ liệu nguyên thủy nào thì bạn nên sử dụng TreeMap Để biết thêm thông tin, hãy tham khảo liên kết này


0

Bạn có thể sử dụng LinkedHashMap<K, V>hoặc bạn có thể triển khai CustomMap của riêng mình để duy trì thứ tự chèn.

Bạn có thể sử dụng CustomHashMaptính năng sau với các tính năng sau:

  • Thứ tự chèn được duy trì, bằng cách sử dụng LinkedHashMap trong nội bộ.
  • Các phím có nullhoặc chuỗi trống không được phép.
  • Khi khóa có giá trị được tạo, chúng tôi sẽ không ghi đè giá trị của nó.

HashMapđấu LinkedHashMapvớiCustomHashMap

interface CustomMap<K, V> extends Map<K, V> {
    public boolean insertionRule(K key, V value);
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public class CustomHashMap<K, V> implements CustomMap<K, V> {
    private Map<K, V> entryMap;
    // SET: Adds the specified element to this set if it is not already present.
    private Set<K> entrySet;

    public CustomHashMap() {
        super();
        entryMap = new LinkedHashMap<K, V>();
        entrySet = new HashSet();
    }

    @Override
    public boolean insertionRule(K key, V value) {
        // KEY as null and EMPTY String is not allowed.
        if (key == null || (key instanceof String && ((String) key).trim().equals("") ) ) {
            return false;
        }

        // If key already available then, we are not overriding its value.
        if (entrySet.contains(key)) { // Then override its value, but we are not allowing
            return false;
        } else { // Add the entry
            entrySet.add(key);
            entryMap.put(key, value);
            return true;
        }
    }
    public V put(K key, V value) {
        V oldValue = entryMap.get(key);
        insertionRule(key, value);
        return oldValue;
    }
    public void putAll(Map<? extends K, ? extends V> t) {
        for (Iterator i = t.keySet().iterator(); i.hasNext();) {
            K key = (K) i.next();
            insertionRule(key, t.get(key));
        }
    }

    public void clear() {
        entryMap.clear();
        entrySet.clear();
    }
    public boolean containsKey(Object key) {
        return entryMap.containsKey(key);
    }
    public boolean containsValue(Object value) {
        return entryMap.containsValue(value);
    }
    public Set entrySet() {
        return entryMap.entrySet();
    }
    public boolean equals(Object o) {
        return entryMap.equals(o);
    }
    public V get(Object key) {
        return entryMap.get(key);
    }
    public int hashCode() {
        return entryMap.hashCode();
    }
    public boolean isEmpty() {
        return entryMap.isEmpty();
    }
    public Set keySet() {
        return entrySet;
    }
    public V remove(Object key) {
        entrySet.remove(key);
        return entryMap.remove(key);
    }
    public int size() {
        return entryMap.size();
    }
    public Collection values() {
        return entryMap.values();
    }
}

Cách sử dụng CustomHashMap:

public static void main(String[] args) {
    System.out.println("== LinkedHashMap ==");
    Map<Object, String> map2 = new LinkedHashMap<Object, String>();
    addData(map2);

    System.out.println("== CustomHashMap ==");
    Map<Object, String> map = new CustomHashMap<Object, String>();
    addData(map);
}
public static void addData(Map<Object, String> map) {
    map.put(null, "1");
    map.put("name", "Yash");
    map.put("1", "1 - Str");
    map.put("1", "2 - Str"); // Overriding value
    map.put("", "1"); // Empty String
    map.put(" ", "1"); // Empty String
    map.put(1, "Int");
    map.put(null, "2"); // Null

    for (Map.Entry<Object, String> entry : map.entrySet()) {
        System.out.println(entry.getKey() + " = " + entry.getValue());
    }
}

O / P:

== LinkedHashMap == | == CustomHashMap ==
null = 2            | name = Yash
name = Yash         | 1 = 1 - Str
1 = 2 - Str         | 1 = Int
 = 1                |
  = 1               |
1 = Int             |

Nếu bạn biết các KEY đã được sửa thì bạn có thể sử dụng EnumMap. Lấy các giá trị biểu mẫu Thuộc tính / tệp XML

VÍ DỤ:

enum ORACLE {
    IP, URL, USER_NAME, PASSWORD, DB_Name;
}

EnumMap<ORACLE, String> props = new EnumMap<ORACLE, String>(ORACLE.class);
props.put(ORACLE.IP, "127.0.0.1");
props.put(ORACLE.URL, "...");
props.put(ORACLE.USER_NAME, "Scott");
props.put(ORACLE.PASSWORD, "Tiget");
props.put(ORACLE.DB_Name, "MyDB");
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.