5

Recently I had an interview to save the huge count of employee details in DS.

I gave the solution as Hashmap with emp Id as key.

The follow up question was if the user wants to search based on name how to implement it. I suggested to use emp name as key and save all the employees with same name as Arraylist.

The next follow up question was tricky, need to create ONE map where user can search based on emp Id or emp name. How to implement this in map?

Implement it in memory efficient way.

Lathy
  • 879
  • 1
  • 8
  • 25
  • Well, how would you start doing it? – Kayaman Sep 22 '15 at 11:27
  • One trick would be to use [name + id] as key. I guess conflicts are not possible if id is an integer and name a String. – Tunaki Sep 22 '15 at 11:27
  • 1
    Probably a better fit for the Programmers site, as this question doesn't involve asking for help with a programming problem. –  Sep 22 '15 at 11:28
  • 1
    @Tunaki how it will help? Imagine I need to retrieve all `Peter`s, what key should I request? – Alex Salauyou Sep 22 '15 at 11:28
  • 1
    @Tunaki Then if I search by Name ?? How to filter ? – Suresh Atta Sep 22 '15 at 11:29
  • @Alex That's a core programming problem for relational database design :) – Suresh Atta Sep 22 '15 at 11:29
  • possible duplicate of [How to implement a Map with multiple keys?](http://stackoverflow.com/questions/822322/how-to-implement-a-map-with-multiple-keys) – Scadge Sep 22 '15 at 11:52
  • @Scadge the solutions provided in this answer only suggest external liberies or multiple maps. – SomeJavaGuy Sep 22 '15 at 11:54
  • @Alex this question is a poor fit for Programmers - it would be quickly voted down and closed over there, see [Why do interview questions make poor Programmers.SE questions?](http://meta.programmers.stackexchange.com/a/6361/31260) Recommended reading: **[What goes on Programmers.SE? A guide for Stack Overflow](http://meta.programmers.stackexchange.com/q/7182/31260)** – gnat Sep 22 '15 at 12:43

4 Answers4

1

This is a dirty solution (yes--very dirty, never do it on production!), but it will work if keys are of different types and one is not subtype of another (e.g. long and String). Put every employee by both keys, and get by provided key, either id or name:

Map<?, List<Employee>> map = new HashMap<>();

public void putEmployee(Employee e) {
    map.put(e.id, Arrays.asList(e));    // put by id
    if (!map.containsKey(e.name)) {
        map.put(e.name, new ArrayList<>());
    }
    map.get(e.name).add(e);             // put by name
}

public Employee getById(long id) {
    return map.containsKey(id) ? map.get(id).get(0) : null;
}

public List<Employee> getByName(String name) {
    return map.containsKey(name) ? map.get(name) : Collections.emptyList();
}

In production code, I'd use two separate maps or custom dictionary class.

Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • I'd run a way for a company that would be happy with something like this. – async Sep 22 '15 at 11:51
  • @async I believe question is about Java features, not about writing clean code. I need to emphasize first and last sentences in the answer. – Alex Salauyou Sep 22 '15 at 11:57
  • I believe this question is just a stupid question. If they were looking to find out if the candidate knows Java features, they should've asked that explicitly, not invent this scenario. – async Sep 22 '15 at 12:01
  • By the way, not judging your solution here, but the question. – async Sep 22 '15 at 12:02
  • @ Sasha : Thank you for your time in giving a solution. But i feel this is like creating a map which will contain the records twice.(One as id & one as name). – Lathy Sep 22 '15 at 12:57
  • @Lathy yes, but they both are references to the same object. Anyway, you'll need two indexes: one for id, another for name. – Alex Salauyou Sep 22 '15 at 13:31
1

This is a pretty easy question to answer: Just convert the IDs to Strings and store employees twice - once under the name and again under the id-as-string.

Your idea of using a List as the value is fine - for IDs, the list would be of size 1.

Note that it would be better to use two maps, because you only ever have one employee per ID and you wouldn't have to deal with a list of size 1 as a degenerate case, so:

Map<Integer, Employee> employeesById;
Map<String, Set<Employee>> employeesByName;

Especially note that you wouldn't use less memory by using just one map. In fact, you would use more memory than storing employees in separate maps for ID keys and name keys.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Thank you for your idea. But storing the employee details twice will make us to consume more memory. Is there any better solution available? – Lathy Sep 23 '15 at 05:25
  • 1
    @Lathy no, you won't consume more memory! Only *references* are stored in the map, not the object itself (there is only one copy of each employee in memory), so all you've got is a map *entry* (really just a pair of references) in another map (you need the entry anyay, no matter which map it's in). Plus, you don't have to create an unnecessary List to store a single entry. The memory usage would be **less** having 2 maps, not more. – Bohemian Sep 23 '15 at 08:36
  • Hmm I agree.. What your opinion on my solution? – Lathy Sep 23 '15 at 09:27
  • @lathy your solution requires a `Map` - mixed key types and mixed value types. If that did the job then why not, but you would have to sacrifice java's strong typing to do it, and it isn't necessary, and doesn't actually help. If the interview question was a "puzzle" not meant to be used in production, then your suction is a good one - it's no worse than the artificial and poor design requirement that you use only one map. – Bohemian Sep 23 '15 at 10:21
  • I have a doubt, you said that in the solution you mentioned above will save name as key but wont the new object(value) override the old value? – Lathy Sep 23 '15 at 10:29
  • @Lathy see edit to show map types. id's are unique, so there's no change of overwriting the value. As for the Set, create a Set on the first put, add the the Set there after. – Bohemian Sep 23 '15 at 11:09
1

I have come up with a solution. Please post your suggestions.

Step 1: Form the hashmap with emp id as key and emp object as value.

Step 2: For the same name create a list of emp id who matches the name Ex: name = 'XYZ' id={101,102,103,...}

Step 3: Insert this name as key and arraylist as value to the same map

Here we are not storing complete employee detail twice. Just trying to maintain a relationship between name and id. So comparatively it could be memory efficient.

Lathy
  • 879
  • 1
  • 8
  • 25
0

One way to do this would be to create a Key object that can be searched by either the name or the id:

public enum KeyType {
    ID, NAME;
}

public class SearchKey {
    private KeyType keyType;
    private String value;

    // constructor and getters snipped for brevity's sake

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        SearchKey searchKey = (SearchKey) o;

        return keyType == searchKey.keyType && value.equals(searchKey.value);

    }

    @Override
    public int hashCode() {
        int result = keyType.hashCode();
        result = 31 * result + value.hashCode();
        return result;
    }

public class Directory {
    private Map<SearchKey, Set<Employee>> directory = new HashMap<>();

    public void addEmployee(Employee e) {
        // Add search by id
        directory.put
            (new SearchKey(KeyType.ID, e.getId()), Collections.singleton(e));

        // Add search by name
        SearchKey searchByName = new SearchKey(KeyType.NAME, e.getName());
        Set<Employee> employees = directory.get(searchByName);
        if (employees == null) {
            employees = new HashSet<>();
            directory.put(searchByName, employees);
        }
        employees.add(e);
    }

    public Employee getById (String id) {
        // Assume that the ID is unique
        return directory.get(new SearchKey(KeyType.ID, id)).iterator().next();
    }

    public Set<Employee> getByName (String name) {
        return directory.get(new SearchKey(KeyType.NAME, name));
    }
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350