làm thế nào để có được một mục từ hashmap mà không cần lặp


172

Có một cách thanh lịch để chỉ lấy một Entry<K,V>từ HashMap, mà không lặp lại, nếu không biết khóa.

Vì thứ tự nhập cảnh không quan trọng, chúng ta có thể nói điều gì đó như

hashMapObject.get(zeroth_index);

Mặc dù tôi biết rằng không tồn tại như vậy bằng phương pháp chỉ mục.

Nếu tôi đã thử cách tiếp cận được đề cập dưới đây, nó vẫn sẽ phải lấy tất cả các mục nhập của hashmap .

for(Map.Entry<String, String> entry : MapObj.entrySet()) {
    return entry;
}

Đề nghị được chào đón.

EDIT: Vui lòng đề xuất bất kỳ Cấu trúc dữ liệu nào khác để đáp ứng yêu cầu.

Câu trả lời:


93

Câu trả lời của Jesper là tốt. Một giải pháp khác là sử dụng TreeMap (bạn đã yêu cầu các cấu trúc dữ liệu khác).

TreeMap<String, String> myMap = new TreeMap<String, String>();
String first = myMap.firstEntry().getValue();
String firstOther = myMap.get(myMap.firstKey());

TreeMap có một chi phí hoạt động để HashMap nhanh hơn, nhưng chỉ là một ví dụ về một giải pháp thay thế.


Hoặc Đồng thờiSkipListMap
Stephen Denne

Vì một số lý do, firstEntry()phương thức này không được xác định trong giao diện SortedMapmà chỉ trong TreeMapConcurrentSkipListMap:(
janko

1
firstEntry () được định nghĩa trong giao diện NavigableMap.
Mỗi

À, cảm ơn, tôi chỉ nhìn SortedMapvì nó có firstKey()phương pháp.
janko

251

Các bản đồ không được đặt hàng, vì vậy không có thứ gọi là 'mục đầu tiên' và đó cũng là lý do tại sao không có phương thức get-by-index trên Map(hoặc HashMap).

Bạn có thể làm điều này:

Map<String, String> map = ...;  // wherever you get this from

// Get the first entry that the iterator returns
Map.Entry<String, String> entry = map.entrySet().iterator().next();

(Lưu ý: Kiểm tra bản đồ trống bị bỏ qua).

Mã của bạn không nhận được tất cả các mục trong bản đồ, nó sẽ trả về ngay lập tức (và thoát ra khỏi vòng lặp) với mục nhập đầu tiên được tìm thấy.

Để in khóa và giá trị của phần tử đầu tiên này:

System.out.println("Key: "+entry.getKey()+", Value: "+entry.getValue());

Lưu ý: Gọi điện iterator()không có nghĩa là bạn đang lặp lại trên toàn bản đồ.


3
Chỉ cần một lưu ý phụ: LinkedHashMapgiữ các phím theo thứ tự chúng được chèn.
Matthieu

2
Có, bản đồ nói chung không được đặt hàng, nhưng một số Maptriển khai như LinkedHashMapTreeMapkhông có thứ tự xác định. ( HashMapkhông).
Jesper

1
Câu trả lời tốt hơn câu trả lời đã chọn - vì câu trả lời được chọn sử dụng TreeMap, dự kiến ​​khóa có thể so sánh được.
Yazad

28

Tôi đoán iterator có thể là giải pháp đơn giản nhất.

return hashMapObject.entrySet().iterator().next();

Một giải pháp khác (không đẹp):

return new ArrayList(hashMapObject.entrySet()).get(0);

Hoặc chưa (không tốt hơn):

return hashMapObject.entrySet().toArray()[0];

7
Không có lý do để sử dụng phiên bản thứ hai hoặc thứ ba. Cái đầu tiên là hoàn toàn tốt, hai cái còn lại lãng phí rất nhiều thời gian bằng cách cung cấp một mảng / Danh sách không có gì.
Joachim Sauer

4
Tôi đồng ý, do đó các bình luận "không đẹp" ;-)
cadrian

14

Nhận giá trị, chuyển đổi nó thành một mảng, lấy phần tử đầu tiên của mảng:

map.values().toArray()[0]

W.


8

Tại sao bạn muốn tránh gọi entrySet()nó thường không tạo ra một đối tượng hoàn toàn mới với bối cảnh riêng của nó, mà thay vào đó chỉ cung cấp một đối tượng mặt tiền. Nói đơn giản entrySet()là một hoạt động khá rẻ.


8

Nếu bạn đang sử dụng Java 8, nó đơn giản như findFirst () :

Ví dụ nhanh:

Optional<Car> theCarFoundOpt = carMap.values().stream().findFirst();

if(theCarFoundOpt.isPresent()) {
    return theCarFoundOpt.get().startEngine();
}

6

Nếu bạn thực sự muốn API mà bạn đề xuất, bạn có thể phân lớp HashMap và theo dõi các khóa trong Danh sách chẳng hạn. Đừng nhìn thấy điểm này, nhưng nó mang lại cho bạn những gì bạn muốn. Nếu bạn giải thích trường hợp sử dụng dự định, có lẽ chúng ta có thể đưa ra một giải pháp tốt hơn.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings("unchecked")
public class IndexedMap extends HashMap {

    private List<Object> keyIndex;

    public IndexedMap() {
        keyIndex = new ArrayList<Object>();
    }

    /**
     * Returns the key at the specified position in this Map's keyIndex.
     * 
     * @param index
     *            index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >= size())
     */
    public Object get(int index) {
        return keyIndex.get(index);
    }

    @Override
    public Object put(Object key, Object value) {

        addKeyToIndex(key);
        return super.put(key, value);
    }

    @Override
    public void putAll(Map source) {

        for (Object key : source.keySet()) {
            addKeyToIndex(key);
        }
        super.putAll(source);
    }

    private void addKeyToIndex(Object key) {

        if (!keyIndex.contains(key)) {
            keyIndex.add(key);
        }
    }

    @Override
    public Object remove(Object key) {

        keyIndex.remove(key);
        return super.remove(key);
    }
}

EDIT: Tôi cố tình không đi sâu vào khía cạnh khái quát của ...


4

Bạn có ý nghĩa gì với "không lặp lại"?

Bạn có thể sử dụng map.entrySet().iterator().next()và bạn sẽ không lặp lại qua bản đồ (theo nghĩa "chạm vào từng đối tượng"). Bạn không thể có được một Entry<K, V>mà không sử dụng một trình vòng lặp mặc dù. Javadoc của Map.Entry nói:

Phương thức Map.entryset trả về chế độ xem bộ sưu tập của bản đồ, có các phần tử thuộc lớp này. Cách duy nhất để có được một tham chiếu đến một mục bản đồ là từ trình vòng lặp của chế độ xem bộ sưu tập này. Các đối tượng Map.Entry này chỉ hợp lệ trong khoảng thời gian lặp.

Bạn có thể giải thích chi tiết hơn, những gì bạn đang cố gắng để đạt được? Nếu bạn muốn xử lý các đối tượng trước, điều đó phù hợp với một tiêu chí cụ thể (như "có một khóa cụ thể") và quay lại các đối tượng còn lại, sau đó xem xét PriorityQueue . Nó sẽ sắp xếp các đối tượng của bạn dựa trên thứ tự tự nhiên hoặc một định nghĩa tùy chỉnh Comparatormà bạn cung cấp.


4
import java.util.*;

public class Friday {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("code", 10);
        map.put("to", 11);
        map.put("joy", 12);

        if (! map.isEmpty()) {
            Map.Entry<String, Integer> entry = map.entrySet().iterator().next();
            System.out.println(entry);
        }
    }
}

Cách tiếp cận này không hiệu quả vì bạn đã sử dụng HashMap. Tôi giả sử sử dụng LinkedHashMap sẽ là giải pháp phù hợp trong trường hợp này.


1
Phương thức nào có trong LinkedHashMap giúp LinkedHashmap hoạt động mà mã này không có?
SL Barth - Phục hồi Monica

3

Điều này sẽ nhận được một mục duy nhất từ ​​bản đồ, gần như có thể nhận được, được đưa ra 'đầu tiên' không thực sự áp dụng.

import java.util.*;

public class Friday {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("code", 10);
        map.put("to", 11);
        map.put("joy", 12);

        if (! map.isEmpty()) {
            Map.Entry<String, Integer> entry = map.entrySet().iterator().next();
            System.out.println(entry);
        }
    }
}

2

Tình cờ tìm thấy điều tương tự ... Sau đó tôi nhớ Iterableslớp học từ thư viện Guava .

Để có được phần tử "đầu tiên" : Iterables.getFirst( someMap.values(), null );.
Về cơ bản cũng giống như Map.values().iterator().next(), nhưng cũng cho phép bạn chỉ định một mặc định (trong trường hợp này là null) nếu không có gì trong Bản đồ.

Iterables.getLast( someMap.values(), null ); trả về phần tử cuối cùng trong Bản đồ.

Iterables.get( someMap.values(), 7, null ); trả về phần tử thứ 7 trong Bản đồ nếu tồn tại, nếu không thì giá trị mặc định (trong trường hợp này là null).

Tuy nhiên, hãy nhớ rằng HashMaps không được đặt hàng ... vì vậy đừng mong đợi Iterables.getFirsttrả lại mục đầu tiên bạn ném vào đó ... tương tự như vậy với Iterables.getLast. Có lẽ hữu ích để có được một giá trị ánh xạ mặc dù.

Có thể không đáng để thêm thư viện Guava cho điều đó, nhưng nếu bạn tình cờ sử dụng một số tiện ích thú vị khác trong thư viện đó ...


1

Theo EDIT của bạn, đây là gợi ý của tôi:

Nếu bạn chỉ có một mục nhập, bạn có thể thay thế Bản đồ bằng một đối tượng kép. Tùy thuộc vào loại và sở thích của bạn:

  • một mảng (gồm 2 giá trị, khóa và giá trị)
  • một đối tượng đơn giản với hai thuộc tính

0
map<string,string>m;
auto it=m.begin();//get iterator to the first element of map(m)
return m->first;//return first string(1st string in map<string,string>m)
//Incase you want the second string 
//you can use return m->second(2st string in map<string,string>m)
//if you want to iterate the whole map you can use loop
for(auto it:m)//m is a map
   cout<<it->first<<endl;

4
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
Alex Riabov

1
Vâng, xin vui lòng thêm một số giải thích cùng với mã của bạn.
jkdev

-3

Tôi nhận được câu trả lời: (Thật đơn giản)

Lấy một ArrayList sau đó bỏ nó và tìm kích thước của danh sách mảng. Đây là:

    ArrayList count = new ArrayList();
    count=(ArrayList) maptabcolname.get("k1"); //here "k1" is Key
    System.out.println("number of elements="+count.size());

Nó sẽ hiển thị kích thước. (Đưa ra gợi ý). Nó đang hoạt động.


2
Làm thế nào để có được một mục tùy ý từ bản đồ băm?
Đại tá Ba mươi Hai
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.