How to return a parameter on the basis on another parameter in list of object

Viewed 50

I have the following InventoryItem class:

@AllArgsConstructor
@Getter
public class InventoryItem {
    private String name;
    private int amount;
    // other properties, getters, etc.
}

And I have an Inventory object that contains a List of InventoryItems.

I want to obtain the amount of the item where the name is equal to the given name.

I am trying to use streams for that purpose:

inventoryItems.stream().filter(item -> item.getName().equals(name));

But it is returning the whole item, I only want the amount. How can I do that? I am new to java so do not have idea.

4 Answers

Use the map() function, append it after the filter.

Optional<int> result = inventoryItems.stream().filter(item -> item.getName().equals(name)).map(item -> item.getAmount()).findFirst();

filter operation is an intermediate operation. We need one of terminal operation to get the result. Below are couple of ways to get the Amount

import java.util.Arrays;
import java.util.List;

class InventoryItem {
    String name;
    float amount;

    public InventoryItem(String name, float amount) {
        this.name = name;
        this.amount = amount;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getAmount() {
        return amount;
    }

    public void setAmount(float amount) {
        this.amount = amount;
    }
}
public class ItemAmount {
    public static void main(String[] args) {
        List<InventoryItem> inventoryItems = Arrays.asList(new InventoryItem("A", 100f), new InventoryItem("B", 50f));

        //method-1
       float amount = inventoryItems.stream().filter(item -> item.getName().equals("B")).findFirst().get().getAmount();
       System.out.println("Amount of B item is: " + amount);

       //method-2
       List<InventoryItem> matchList = inventoryItems.stream().filter(item -> item.getName().equals("B")).collect(Collectors.toList());
       matchList.forEach(i -> System.out.println("Item: " + i.getName() + ", Amount: " + i.getAmount()));
    }
}

There are to issues:

  • Your stream lacks a terminal operation which is needed to obtain the result of the stream execution.

  • To amount from an InventoryItem you need to apply map() operation.

That's how you can generate a list of amounts that correspond to the items having the given name:

public List<Integer> getAmountsByName(List<InventoryItem> inventoryItems,
                                             String name) {
    return inventoryItems.stream()
        .filter(item -> item.getName().equals(name))
        .map(InventoryItem::getAmount)
        .toList(); // for Java 16+ or collect(Collectors.toList()) for earlier versions
}

In case if names of items are expected to be unique, then you can apply findFirst() as a terminal operation, which returns an Optional as a result (because resulting value might not be present).

There are many possibilities how you can dial the Optional, for instance you can use orElse() to provide the default value, or orElseThrow() to throw an exception if a result was not found (sometimes it makes sense to return an Optional, and deal with it in the calling method).

That's how it might look like:

public int getAmountByName(List<InventoryItem> inventoryItems,
                           String name) {
        
    return inventoryItems.stream()
        .filter(item -> item.getName().equals(name))
        .map(InventoryItem::getAmount)
        .findFirst()
        .orElse(-1); // in case if an item with the target name isn't present return zero
        //    .orElseThrow(); // alternatively you can throw an exception depending on your needs
}

Using JDK 14:

I have implemented the below code using records instead of creating POJO's.

Note (As a suggestion): As I can see in your given code, you are using some annotations(@AllArgsConstructor @Getter,..) for auto creation of constructors, getters and setters, so for that boiler-plate code, records seems to be a good option which is available from java 14 onwards.

Records in java : As of JDK 14, we can replace our data classes with records. Records are immutable classes that require only the type and name of fields. We do not need to create constructor, getters, setters, override toString() methods, override hashcode and equals methods.(records javadoc)

Please find the code below:

public class Test {
        
          public static void main(String[] args) {
        
            String nameToFind = "item1";
            int amount = 0;
        
            record InventoryItem(String name,int amount){}
        
            Optional<Integer> optional = List.of(new InventoryItem("item1",100),
                    new InventoryItem("item2",200),
                    new InventoryItem("item3",300))
                    .stream().filter(x -> x.name().equals(nameToFind)).map(InventoryItem::amount).findFirst();
            
            if(optional.isPresent()){
              amount = optional.get();
            }
        
            System.out.println(amount);
          }
Related