How can I store <String, List<String>> in Java?

Viewed 122

Let's say I have to implement the menu of a restaurant and I have different dish names with their respective ingredients.

For example:

Classic: strawberry, banana, pineapple, mango, peach, honey, ice, yogurt
Forest Berry: strawberry, raspberry, blueberry, honey, ice, yogurt
...

In python, I would use a dictionary to implement such things. Later on, with this data structure, I would need to write a method that given a string containing first the name of the dish (the key, if I use a dictionary) and an optional list of ingredients separated by commas, returns the ingredients of the dish minus the ones (if) included in the string.

For example, let's run the method ingredients with the following configuration:

Smoothie.ingredients("Classic,-strawberry,-banana")

The out I would need would be:

"honey,ice,mango,peach,pineapple,yogurt"

I am looking to the most convenient Java data structure to store this kind of object and perform this kind of operation.

2 Answers

You should use a Map<String,Set<String>>. As it is a key-value data structure.

As for the ingredients I would suggest Set<String>, because we don't care about their order, but all ingredients must be unique.

Below code works for JDK11, but can be refactored to work in older versions also:

// we normalize key to upper case, to be case insensitive
private Map<String, Set<String>> menu = new HashMap<>();

@BeforeEach
void setUp() { // initialize your menu
    menu.put("CLASSIC", Set.of("strawberry", "banana", "pineapple", "mango", "peach", "honey", "ice", "yogurt"));
    menu.put("FOREST BERRY", Set.of("strawberry", "raspberry", "blueberry", "honey", "ice", "yogurt"));
}

@Test
void getMissingingredients() {
    String key = "classic";
    Set<String> startIngredients = Set.of("strawberry", "banana");
    // when
    Set<String> ingredients = getMissingIngredients(key, startIngredients);
    // then
    assertEquals(Set.of("pineapple", "mango", "peach", "honey", "ice", "yogurt"), ingredients);
}

private Set<String> getMissingIngredients(String dishName, Set<String> initialIngredients) {
    
    // find dish ingredients by dish name. Copy them to a new HashSet
    Set<String> ingredients = new HashSet<>(menu.getOrDefault(dishName.toUpperCase(), Collections.emptySet()));

    // remove ingredients that you already have
    ingredients.removeAll(initialIngredients);

    return ingredients;
}

How can I store <String, List> in Java?

That's a multimap, and Guava (3rd-party Java library) offers an implementation of it.

Given your example, you could then use it as in

public static void main(String[] args) {

    Multimap<String, String> multimap = TreeMultimap.create();
    multimap.putAll("Classic", Arrays.asList("strawberry", "banana",
            "pineapple", "mango", "peach", "honey", "ice", "yogurt") );
    multimap.putAll("Forest Berry", Arrays.asList("strawberry", "raspberry",
            "blueberry", "honey", "ice", "yogurt") );

    String result = multimap.get("Classic")
            .stream()
            .filter(e -> !e.equals("strawberry") && !e.equals("banana"))
            .collect(Collectors.joining(", "));

    System.out.println(result);

}

which then gives you the desired sample output

honey, ice, mango, peach, pineapple, yogurt

From here, you then can generalize further.

(Note that TreeMultimap is an implementation which does not store duplicate key-value pairs, and the keys and values are ordered by their natural ordering or by supplied comparators.)

Related