[英]JAVA : Best performance-wise method to find an object stored in hashMap
我有一堆對象存儲在hashMap<Long,Person>
我需要找到具有特定屬性的person對象而不知道它的ID。
例如人類:
public person{
long id;
String firstName;
String lastName;
String userName;
String password;
String address;
..
(around 7-10 attributes in total)
}
假設我想找到username =“mike”的對象。 是否有任何方法可以找到它而不實際迭代整個哈希映射,如下所示:
for (Map.Entry<Long,Person> entry : map.entrySet()) {
if(entry.getValue().getUserName().equalsIgnoreCase("mike"));
我在這里找到的答案很古老。
如果你想要速度並且總是在尋找一個特定屬性,那么最好的辦法是創建另一個用該屬性鍵入的“緩存”哈希映射。
對於不到一百萬個條目,占用的內存將是無關緊要的,並且哈希映射查找將比任何其他解決方案快得多。
或者,您可以將所有搜索屬性放入單個映射(即名稱和ID)。 如果您關心碰撞,請使用獨特的內容為密鑰添加前綴。 就像是:
String ID_PREFIX = "^!^ID^!^";
String USERNAME_PREFIX = "^!^USERNAME^!^";
String FIRSTNAME_PREFIX = "^!^FIRSTNAME^!^";
Map<String,Person> personMap = new HashMap<String,Person>();
//add a person
void addPersonToMap(Person person)
{
personMap.put(ID_PREFIX+person.id, person);
personMap.put(USERNAME_PREFIX+person.username, person);
personMap.put(FIRSTNAME_PREFIX+person.firstname, person);
}
//search person
Person findPersonByID(long id)
{
return personMap.get(ID_PREFIX+id);
}
Person findPersonByUsername(String username)
{
return personMap.get(USERNAME_PREFIX+username);
}
//or a more generic version:
//Person foundPerson = findPersonByAttribute(FIRSTNAME_PREFIX, "mike");
Person findPersonByAttribute(String attr, String attr_value)
{
return personMap.get(attr+attr_value);
}
以上假設每個屬性在所有人中都是唯一的。 這可能適用於ID和用戶名,但問題是指定firstname = mike,這不太可能是唯一的。
在這種情況下,你想用列表進行抽象,所以它更像是這樣:
Map<String,List<Person>> personMap = new HashMap<String,List<Person>>();
//add a person
void addPersonToMap(Person person)
{
insertPersonIntoMap(ID_PREFIX+person.id, person);
insertPersonIntoMap(USERNAME_PREFIX+person.username, person);
insertPersonIntoMap(FIRSTNAME_PREFIX+person.firstname, person);
}
//note that List contains no duplicates, so can be called multiple times for the same person.
void insertPersonIntoMap(String key, Person person)
{
List<Person> personsList = personMap.get(key);
if(personsList==null)
personsList = new ArrayList<Person>();
personsList.add(person);
personMap.put(key,personsList);
}
//we know id is unique, so we can just get the only person in the list
Person findPersonByID(long id)
{
List<Person> personList = personMap.get(ID_PREFIX+id);
if(personList!=null)
return personList.get(0);
return null;
}
//get list of persons with firstname
List<Person> findPersonsByFirstName(String firstname)
{
return personMap.get(FIRSTNAME_PREFIX+firstname);
}
在那一點上,你真的進入了一個抓包設計,但如果你不期待數百萬條款,它仍然非常有效。
我能想到的最佳性能方法是使用另一個HashMap
,其中鍵是要搜索的屬性,值是對象列表。
對於您的示例,這將是HashMap<String, List<Person>>
,其中鍵是用戶名。 缺點是您必須維護兩張地圖。
注意:我使用List<Person>
作為值,因為我們無法保證username
在所有用戶中都是唯一的。 這同樣適用於任何其他領域。
例如,要將Person
添加到此新地圖,您可以執行以下操作:
Map<String, List<Person>> peopleByUsername = new HashMap<>();
// ...
Person p = ...;
peopleByUsername.computeIfAbsent(
p.getUsername(),
k -> new ArrayList<>())
.add(p);
然后,返回所有用戶名為joesmith
:
List<Person> matching = peopleByUsername.get("joesmith");
從易失性地圖中獲取一個或幾個條目
如果您正在操作的地圖可以經常更改,並且您只想獲得一些條目,那么迭代地圖的條目就可以了,因為您需要空間和時間來構建其他結構或對數據進行排序。
從易失性地圖中獲取許多條目
如果您需要從該映射中獲取許多條目,則可以通過首先對條目進行排序(例如,構建列表並對其進行排序)然后使用二進制搜索來獲得更好的性能。 或者,您可以構建一個中間映射,使用您需要搜索的屬性作為其鍵。
但請注意,這兩種方法至少需要時間,因此當您查找許多條目時,這只會產生更好的性能。
從“持久性”地圖中多次獲取條目
如果您的地圖及其估值值沒有變化(或者不經常變化),您可以維護地圖屬性 - >人。 這意味着初始設置和更新附加映射(除非您的數據不會更改)以及一些內存開銷需要一些努力,但以后會大大加快查找速度。 與查找頻率相比,當您執行非常少的“寫入”時,這是一種有價值的方法,如果您可以節省內存開銷(取決於這些映射的大小以及您需要多少內存)。
考慮每個備用密鑰一個哈希映射。 這將具有“高”設置成本,但將導致通過備用密鑰快速檢索。
Long
鍵值設置hashmap。 Person
對象並創建第二個hashmap( HashMap<String, Person>
),其中username是密鑰。 也許,同時填充兩個哈希圖。
在您的情況下,您將得到類似HashMap<Long, Person> idKeyedMap
和HashMap<String, Person> usernameKeyedMap
。
如果將地圖定義為Map<Object, Person>
也可以將所有鍵值放在同一個映射中。 然后,當您添加(id,person)對時,您還需要添加(用戶名,人物)對。 警告,這不是一個偉大的技術。
解決問題的最佳方法是什么?
正如您在答案和評論中看到的,有很多方法可以解決這個問題。
如何使用Map(也許是如何創建它)。 如果Map是從select語句構建的,並且表中的列具有long
id值,我們可能會認為我們應該使用HashMap<Long, Person>
。
查看問題的另一種方法是考慮用戶名也應該是唯一的(即,任何兩個人都不應該共享相同的用戶名)。 因此,將地圖創建為HashMap<String, Person>
。 使用username作為鍵,Person對象作為值。
使用后者:
Map<String, Person> users = new HashMap<>();
users = retrieveUsersFromDatabase(); // perform db select and build map
String username = "mike";
users.get(username).
這將是在包含Person對象作為其值的Map中檢索要查找的對象的最快方法。
您可以使用以下命令將Hashmap簡單地轉換為List:
List list = new ArrayList(map.values());
現在,您可以輕松地遍歷列表對象。 這樣,您可以在Person類的任何屬性上搜索Hashmap值,而不僅僅是限制firstname。
唯一的缺點是你最終會創建一個列表對象。 但是使用stream api可以進一步改進代碼,將Hashmap轉換為列表並在單個操作中進行迭代,從而節省空間並提高並行流的性能。
可以通過設計和使用適當的Comparator類來完成對值對象的排序和查找。
比較器類:根據特定屬性設計比較器可以按如下方式進行:
class UserComparator implements Comparator<Person>{
@Override
public int compare(Person p1, Person p2) {
return p1.userName.compareTo(p2.userName);
}
}
用法:上面設計的比較器可以使用如下:
HashMap<Long, Person> personMap = new HashMap<Long, Person>();
.
.
.
ArrayList<Person> pAL = new ArrayList<Person>(personMap.values()); //create list of values
Collections.sort(pAL,new UserComparator()); // sort the list using comparator
Person p = new Person(); // create a dummy object
p.userName="mike"; // Only set the username
int i= Collections.binarySearch(pAL,p,new UserComparator()); // search the list using comparator
if(i>=0){
Person p1 = pAL.get(Collections.binarySearch(pAL,p,new UserComparator())); //Obtain object if username is present
}else{
System.out.println("Insertion point: "+ i); // Returns a negative value if username is not present
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.