karan mirani
karan mirani

Reputation: 188

Java 8 in Action Streams distinct clarification

I was going through the exercises in Java 8 in Action and I came across this question. There are 2 classes Trader and Transaction described as follows:

public class Trader {
    private final String name;
    private final String city;

    public Trader(String n, String c) {
        this.name = n;
        this.city = c;
    }

    public String getName() {
        return this.name;
    }

    public String getCity() {
        return this.city;
    }

    public String toString() {
        return "Trader:" + this.name + " in " + this.city;
    }
}
public class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return this.trader;
    }

    public int getYear() {
        return this.year;
    }

    public int getValue() {
        return this.value;
    }

    public String toString() {
        return "{" + this.trader + ", " + "year: " + this.year + ", " + "value:" + this.value + "}";
    }
}

Traders and a list of transactions are created as follows:

        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");
        Trader raoul = new Trader("Raoul", "Cambridge");
        List<Transaction> transactions = Arrays.asList(new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000), new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710), new Transaction(mario, 2012, 700), new Transaction(alan, 2012, 950));

The question is to find all traders from Cambridge and sort them by name. The solution given to this problem in the book is as follows:

List<Trader> traders = transactions.stream()
                               .map(Transaction::getTrader)
                               .filter(trader -> trader.getCity().equals("Cambridge"))
                               .distinct()
                               .sorted(comparing(Trader::getName))
                               .collect(toList());

The answer provided above returns the correct results but I was wondering how and why is distinct() using the name field to return distinct traders?

Upvotes: 0

Views: 143

Answers (2)

Eran
Eran

Reputation: 393906

distinct() is not using name field to return distinct traders. It uses the equals method to determine if two Traders are identical. Since Trader doesn't override equals, the only reason this code appears to work is that it passes the same instance (referenced by raoul) to two Transactions, so the default equals implementations (which checks ==) is sufficient to determine that they are identical.

Hence the output List is:

[Trader:Alan in Cambridge, Trader:Brian in Cambridge, Trader:Raoul in Cambridge]

However, if you make the following change:

Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
Trader raoul = new Trader("Raoul", "Cambridge");
Trader raoul2 = new Trader("Raoul", "Cambridge");
List<Transaction> transactions = Arrays.asList(new Transaction(brian, 2011, 300),
        new Transaction(raoul, 2012, 1000), new Transaction(raoul2, 2011, 400),
        new Transaction(mario, 2012, 710), new Transaction(mario, 2012, 700), new Transaction(alan, 2012, 950));

You'll see that now, since raoul and raoul2 are not equal (even though they have the same name), they will both appear in the output List:

[Trader:Alan in Cambridge, Trader:Brian in Cambridge, Trader:Raoul in Cambridge, Trader:Raoul in Cambridge]

Upvotes: 4

Vitalik K
Vitalik K

Reputation: 11

Stream.distinct() compare objects by Object.equals(Object) https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#distinct--

in this example, you have 4 Trader objects which are compared by the default implementation of Object.equals() since Trades class doesn't override equals method

Upvotes: 1

Related Questions