Java 8 lambda filtering based on condition as well as order











up vote
16
down vote

favorite
2












I was trying to filter a list based on multiple conditions, sorting.



class Student{
private int Age;
private String className;
private String Name;

public Student(int age, String className, String name) {
Age = age;
this.className = className;
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}
}


Now if I have a list of that, say



List<Student> students = new ArrayList<>();
students.add(new Student(24, "A", "Smith"));
students.add(new Student(24, "A", "John"));
students.add(new Student(30, "A", "John"));
students.add(new Student(20, "B", "John"));
students.add(new Student(24, "B", "Prince"));


How would I be able to get a list of the oldest students with a distinct name?
In C# this would be quite simple by using System.Linq GroupBy then comparing and then flattening with select, I'm not too sure how I could achieve the same in Java.










share|improve this question


















  • 7




    students.stream().collect(groupingBy(...)).
    – Andy Turner
    Dec 5 at 14:56










  • Do you only want a list of the oldest and only the oldest ones?
    – michaeak
    Dec 5 at 15:07










  • Please tell what is the desired result. I.e. How the map/list/whatever looks like.
    – michaeak
    Dec 5 at 15:38

















up vote
16
down vote

favorite
2












I was trying to filter a list based on multiple conditions, sorting.



class Student{
private int Age;
private String className;
private String Name;

public Student(int age, String className, String name) {
Age = age;
this.className = className;
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}
}


Now if I have a list of that, say



List<Student> students = new ArrayList<>();
students.add(new Student(24, "A", "Smith"));
students.add(new Student(24, "A", "John"));
students.add(new Student(30, "A", "John"));
students.add(new Student(20, "B", "John"));
students.add(new Student(24, "B", "Prince"));


How would I be able to get a list of the oldest students with a distinct name?
In C# this would be quite simple by using System.Linq GroupBy then comparing and then flattening with select, I'm not too sure how I could achieve the same in Java.










share|improve this question


















  • 7




    students.stream().collect(groupingBy(...)).
    – Andy Turner
    Dec 5 at 14:56










  • Do you only want a list of the oldest and only the oldest ones?
    – michaeak
    Dec 5 at 15:07










  • Please tell what is the desired result. I.e. How the map/list/whatever looks like.
    – michaeak
    Dec 5 at 15:38















up vote
16
down vote

favorite
2









up vote
16
down vote

favorite
2






2





I was trying to filter a list based on multiple conditions, sorting.



class Student{
private int Age;
private String className;
private String Name;

public Student(int age, String className, String name) {
Age = age;
this.className = className;
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}
}


Now if I have a list of that, say



List<Student> students = new ArrayList<>();
students.add(new Student(24, "A", "Smith"));
students.add(new Student(24, "A", "John"));
students.add(new Student(30, "A", "John"));
students.add(new Student(20, "B", "John"));
students.add(new Student(24, "B", "Prince"));


How would I be able to get a list of the oldest students with a distinct name?
In C# this would be quite simple by using System.Linq GroupBy then comparing and then flattening with select, I'm not too sure how I could achieve the same in Java.










share|improve this question













I was trying to filter a list based on multiple conditions, sorting.



class Student{
private int Age;
private String className;
private String Name;

public Student(int age, String className, String name) {
Age = age;
this.className = className;
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}

public String getClassName() {
return className;
}

public void setClassName(String className) {
this.className = className;
}

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}
}


Now if I have a list of that, say



List<Student> students = new ArrayList<>();
students.add(new Student(24, "A", "Smith"));
students.add(new Student(24, "A", "John"));
students.add(new Student(30, "A", "John"));
students.add(new Student(20, "B", "John"));
students.add(new Student(24, "B", "Prince"));


How would I be able to get a list of the oldest students with a distinct name?
In C# this would be quite simple by using System.Linq GroupBy then comparing and then flattening with select, I'm not too sure how I could achieve the same in Java.







java lambda java-8 java-stream






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Dec 5 at 14:55









The 0bserver

17710




17710








  • 7




    students.stream().collect(groupingBy(...)).
    – Andy Turner
    Dec 5 at 14:56










  • Do you only want a list of the oldest and only the oldest ones?
    – michaeak
    Dec 5 at 15:07










  • Please tell what is the desired result. I.e. How the map/list/whatever looks like.
    – michaeak
    Dec 5 at 15:38
















  • 7




    students.stream().collect(groupingBy(...)).
    – Andy Turner
    Dec 5 at 14:56










  • Do you only want a list of the oldest and only the oldest ones?
    – michaeak
    Dec 5 at 15:07










  • Please tell what is the desired result. I.e. How the map/list/whatever looks like.
    – michaeak
    Dec 5 at 15:38










7




7




students.stream().collect(groupingBy(...)).
– Andy Turner
Dec 5 at 14:56




students.stream().collect(groupingBy(...)).
– Andy Turner
Dec 5 at 14:56












Do you only want a list of the oldest and only the oldest ones?
– michaeak
Dec 5 at 15:07




Do you only want a list of the oldest and only the oldest ones?
– michaeak
Dec 5 at 15:07












Please tell what is the desired result. I.e. How the map/list/whatever looks like.
– michaeak
Dec 5 at 15:38






Please tell what is the desired result. I.e. How the map/list/whatever looks like.
– michaeak
Dec 5 at 15:38














4 Answers
4






active

oldest

votes

















up vote
15
down vote













Use the toMap collector:



Collection<Student> values = students.stream()
.collect(toMap(Student::getName,
Function.identity(),
BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
.values();


Explanation



We're using this overload of toMap:



toMap​(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction)




  • Student::getName above is the keyMapper function used to extract the values for the map keys.


  • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.


  • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .

  • Finally, invoking values() returns us a collection of students.


The equivalent C# code being:



var values = students.GroupBy(s => s.Name, v => v,
(a, b) => b.OrderByDescending(e => e.Age).Take(1))
.SelectMany(x => x);


Explanation (for those unfamiliar with .NET)



We're using this extension method of GroupBy:



System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
(this System.Collections.Generic.IEnumerable<TSource> source,
Func<TSource,TKey> keySelector,
Func<TSource,TElement> elementSelector,
Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);




  • s => s.Name above is the keySelector function used to extract the value to group by.


  • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.


  • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.

  • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.






share|improve this answer























  • Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
    – Luis G.
    Dec 5 at 17:08


















up vote
5
down vote













Or without streams:



Map<String, Student> map = new HashMap<>();
students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
Collection<Student> max = map.values();





share|improve this answer























  • Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
    – nullpointer
    Dec 5 at 15:27






  • 3




    @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
    – Federico Peralta Schaffner
    Dec 5 at 15:45




















up vote
1
down vote













If you need a grouping only sorted, it is quite simple:



Map<String, List<Student>> collect = students.stream() // stream capabilities
.sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
.collect(Collectors.groupingBy(Student::getName)); // group by name.


Output in collect:




  • Prince=[Student [Age=24, className=B, Name=Prince]],

  • Smith=[Student [Age=24, className=A, Name=Smith]],

  • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]






share|improve this answer






























    up vote
    1
    down vote













    Just to mix and merge the other solutions, you could alternatively do :



    Map<String, Student> nameToStudentMap = new HashMap<>();
    Set<Student> finalListOfStudents = students.stream()
    .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
    .collect(Collectors.toSet());





    share|improve this answer



















    • 3




      This violates the requirement of statelessness on the parameter of Stream.map.
      – Andy Turner
      Dec 5 at 22:00













    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53635022%2fjava-8-lambda-filtering-based-on-condition-as-well-as-order%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    15
    down vote













    Use the toMap collector:



    Collection<Student> values = students.stream()
    .collect(toMap(Student::getName,
    Function.identity(),
    BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
    .values();


    Explanation



    We're using this overload of toMap:



    toMap​(Function<? super T,? extends K> keyMapper,
    Function<? super T,? extends U> valueMapper,
    BinaryOperator<U> mergeFunction)




    • Student::getName above is the keyMapper function used to extract the values for the map keys.


    • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.


    • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .

    • Finally, invoking values() returns us a collection of students.


    The equivalent C# code being:



    var values = students.GroupBy(s => s.Name, v => v,
    (a, b) => b.OrderByDescending(e => e.Age).Take(1))
    .SelectMany(x => x);


    Explanation (for those unfamiliar with .NET)



    We're using this extension method of GroupBy:



    System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
    (this System.Collections.Generic.IEnumerable<TSource> source,
    Func<TSource,TKey> keySelector,
    Func<TSource,TElement> elementSelector,
    Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);




    • s => s.Name above is the keySelector function used to extract the value to group by.


    • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.


    • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.

    • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.






    share|improve this answer























    • Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
      – Luis G.
      Dec 5 at 17:08















    up vote
    15
    down vote













    Use the toMap collector:



    Collection<Student> values = students.stream()
    .collect(toMap(Student::getName,
    Function.identity(),
    BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
    .values();


    Explanation



    We're using this overload of toMap:



    toMap​(Function<? super T,? extends K> keyMapper,
    Function<? super T,? extends U> valueMapper,
    BinaryOperator<U> mergeFunction)




    • Student::getName above is the keyMapper function used to extract the values for the map keys.


    • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.


    • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .

    • Finally, invoking values() returns us a collection of students.


    The equivalent C# code being:



    var values = students.GroupBy(s => s.Name, v => v,
    (a, b) => b.OrderByDescending(e => e.Age).Take(1))
    .SelectMany(x => x);


    Explanation (for those unfamiliar with .NET)



    We're using this extension method of GroupBy:



    System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
    (this System.Collections.Generic.IEnumerable<TSource> source,
    Func<TSource,TKey> keySelector,
    Func<TSource,TElement> elementSelector,
    Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);




    • s => s.Name above is the keySelector function used to extract the value to group by.


    • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.


    • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.

    • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.






    share|improve this answer























    • Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
      – Luis G.
      Dec 5 at 17:08













    up vote
    15
    down vote










    up vote
    15
    down vote









    Use the toMap collector:



    Collection<Student> values = students.stream()
    .collect(toMap(Student::getName,
    Function.identity(),
    BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
    .values();


    Explanation



    We're using this overload of toMap:



    toMap​(Function<? super T,? extends K> keyMapper,
    Function<? super T,? extends U> valueMapper,
    BinaryOperator<U> mergeFunction)




    • Student::getName above is the keyMapper function used to extract the values for the map keys.


    • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.


    • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .

    • Finally, invoking values() returns us a collection of students.


    The equivalent C# code being:



    var values = students.GroupBy(s => s.Name, v => v,
    (a, b) => b.OrderByDescending(e => e.Age).Take(1))
    .SelectMany(x => x);


    Explanation (for those unfamiliar with .NET)



    We're using this extension method of GroupBy:



    System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
    (this System.Collections.Generic.IEnumerable<TSource> source,
    Func<TSource,TKey> keySelector,
    Func<TSource,TElement> elementSelector,
    Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);




    • s => s.Name above is the keySelector function used to extract the value to group by.


    • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.


    • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.

    • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.






    share|improve this answer














    Use the toMap collector:



    Collection<Student> values = students.stream()
    .collect(toMap(Student::getName,
    Function.identity(),
    BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))))
    .values();


    Explanation



    We're using this overload of toMap:



    toMap​(Function<? super T,? extends K> keyMapper,
    Function<? super T,? extends U> valueMapper,
    BinaryOperator<U> mergeFunction)




    • Student::getName above is the keyMapper function used to extract the values for the map keys.


    • Function.identity() above is the valueMapper function used to extract the values for the map values where Function.identity() simply returns the elements in the source them selves i.e. the Student objects.


    • BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)) above is the merge function used to "decide which Student object to return in the case of a key collission i.e. when two given students have the same name" in this case taking the oldest Student .

    • Finally, invoking values() returns us a collection of students.


    The equivalent C# code being:



    var values = students.GroupBy(s => s.Name, v => v,
    (a, b) => b.OrderByDescending(e => e.Age).Take(1))
    .SelectMany(x => x);


    Explanation (for those unfamiliar with .NET)



    We're using this extension method of GroupBy:



    System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource,TKey,TElement,TResult> 
    (this System.Collections.Generic.IEnumerable<TSource> source,
    Func<TSource,TKey> keySelector,
    Func<TSource,TElement> elementSelector,
    Func<TKey,System.Collections.Generic.IEnumerable<TElement>,TResult> resultSelector);




    • s => s.Name above is the keySelector function used to extract the value to group by.


    • v => v above is the elementSelector function used to extract the values i.e. the Student objects them selves.


    • b.OrderByDescending(e => e.Age).Take(1) above is the resultSelector which given an IEnumerable<Student> represented as b takes the oldest student.

    • Finally, we apply .SelectMany(x => x); to collapse the resulting IEnumerable<IEnumerable<Student>> into a IEnumerable<Student>.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 5 at 15:37

























    answered Dec 5 at 14:56









    Aomine

    35.8k62960




    35.8k62960












    • Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
      – Luis G.
      Dec 5 at 17:08


















    • Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
      – Luis G.
      Dec 5 at 17:08
















    Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
    – Luis G.
    Dec 5 at 17:08




    Nice answer. Just wanted to say that using v -> v instead of Function.identity() would make the code more similar to the C# equivalent and so easier to compare.
    – Luis G.
    Dec 5 at 17:08












    up vote
    5
    down vote













    Or without streams:



    Map<String, Student> map = new HashMap<>();
    students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
    Collection<Student> max = map.values();





    share|improve this answer























    • Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
      – nullpointer
      Dec 5 at 15:27






    • 3




      @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
      – Federico Peralta Schaffner
      Dec 5 at 15:45

















    up vote
    5
    down vote













    Or without streams:



    Map<String, Student> map = new HashMap<>();
    students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
    Collection<Student> max = map.values();





    share|improve this answer























    • Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
      – nullpointer
      Dec 5 at 15:27






    • 3




      @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
      – Federico Peralta Schaffner
      Dec 5 at 15:45















    up vote
    5
    down vote










    up vote
    5
    down vote









    Or without streams:



    Map<String, Student> map = new HashMap<>();
    students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
    Collection<Student> max = map.values();





    share|improve this answer














    Or without streams:



    Map<String, Student> map = new HashMap<>();
    students.forEach(x -> map.merge(x.getName(), x, (oldV, newV) -> oldV.getAge() > newV.getAge() ? oldV : newV));
    Collection<Student> max = map.values();






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 5 at 15:18

























    answered Dec 5 at 15:09









    Eugene

    67.6k997160




    67.6k997160












    • Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
      – nullpointer
      Dec 5 at 15:27






    • 3




      @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
      – Federico Peralta Schaffner
      Dec 5 at 15:45




















    • Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
      – nullpointer
      Dec 5 at 15:27






    • 3




      @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
      – Federico Peralta Schaffner
      Dec 5 at 15:45


















    Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
    – nullpointer
    Dec 5 at 15:27




    Thought of this while starting of from your initial suggestion, but then an additional Map here, but does map.merge save us compared to toMap?
    – nullpointer
    Dec 5 at 15:27




    3




    3




    @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
    – Federico Peralta Schaffner
    Dec 5 at 15:45






    @nullpointer This should have been my answer (I'm supposedly the without streams guy here), so I feel I can answer your question... Actually, Collectors.toMap uses Map.merge under the hood. It even says so in the docs: mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction).
    – Federico Peralta Schaffner
    Dec 5 at 15:45












    up vote
    1
    down vote













    If you need a grouping only sorted, it is quite simple:



    Map<String, List<Student>> collect = students.stream() // stream capabilities
    .sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
    .collect(Collectors.groupingBy(Student::getName)); // group by name.


    Output in collect:




    • Prince=[Student [Age=24, className=B, Name=Prince]],

    • Smith=[Student [Age=24, className=A, Name=Smith]],

    • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]






    share|improve this answer



























      up vote
      1
      down vote













      If you need a grouping only sorted, it is quite simple:



      Map<String, List<Student>> collect = students.stream() // stream capabilities
      .sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
      .collect(Collectors.groupingBy(Student::getName)); // group by name.


      Output in collect:




      • Prince=[Student [Age=24, className=B, Name=Prince]],

      • Smith=[Student [Age=24, className=A, Name=Smith]],

      • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]






      share|improve this answer

























        up vote
        1
        down vote










        up vote
        1
        down vote









        If you need a grouping only sorted, it is quite simple:



        Map<String, List<Student>> collect = students.stream() // stream capabilities
        .sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
        .collect(Collectors.groupingBy(Student::getName)); // group by name.


        Output in collect:




        • Prince=[Student [Age=24, className=B, Name=Prince]],

        • Smith=[Student [Age=24, className=A, Name=Smith]],

        • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]






        share|improve this answer














        If you need a grouping only sorted, it is quite simple:



        Map<String, List<Student>> collect = students.stream() // stream capabilities
        .sorted(Comparator.comparingInt(Student::getAge).reversed()) // sort by age, descending
        .collect(Collectors.groupingBy(Student::getName)); // group by name.


        Output in collect:




        • Prince=[Student [Age=24, className=B, Name=Prince]],

        • Smith=[Student [Age=24, className=A, Name=Smith]],

        • John=[Student [Age=30, className=A, Name=John], Student [Age=24, className=A, Name=John], Student [Age=20, className=B, Name=John]]







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 5 at 15:45

























        answered Dec 5 at 14:57









        michaeak

        739315




        739315






















            up vote
            1
            down vote













            Just to mix and merge the other solutions, you could alternatively do :



            Map<String, Student> nameToStudentMap = new HashMap<>();
            Set<Student> finalListOfStudents = students.stream()
            .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
            .collect(Collectors.toSet());





            share|improve this answer



















            • 3




              This violates the requirement of statelessness on the parameter of Stream.map.
              – Andy Turner
              Dec 5 at 22:00

















            up vote
            1
            down vote













            Just to mix and merge the other solutions, you could alternatively do :



            Map<String, Student> nameToStudentMap = new HashMap<>();
            Set<Student> finalListOfStudents = students.stream()
            .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
            .collect(Collectors.toSet());





            share|improve this answer



















            • 3




              This violates the requirement of statelessness on the parameter of Stream.map.
              – Andy Turner
              Dec 5 at 22:00















            up vote
            1
            down vote










            up vote
            1
            down vote









            Just to mix and merge the other solutions, you could alternatively do :



            Map<String, Student> nameToStudentMap = new HashMap<>();
            Set<Student> finalListOfStudents = students.stream()
            .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
            .collect(Collectors.toSet());





            share|improve this answer














            Just to mix and merge the other solutions, you could alternatively do :



            Map<String, Student> nameToStudentMap = new HashMap<>();
            Set<Student> finalListOfStudents = students.stream()
            .map(x -> nameToStudentMap.merge(x.getName(), x, (a, b) -> a.getAge() > b.getAge() ? a : b))
            .collect(Collectors.toSet());






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Dec 5 at 16:17

























            answered Dec 5 at 16:08









            nullpointer

            38.2k1073146




            38.2k1073146








            • 3




              This violates the requirement of statelessness on the parameter of Stream.map.
              – Andy Turner
              Dec 5 at 22:00
















            • 3




              This violates the requirement of statelessness on the parameter of Stream.map.
              – Andy Turner
              Dec 5 at 22:00










            3




            3




            This violates the requirement of statelessness on the parameter of Stream.map.
            – Andy Turner
            Dec 5 at 22:00






            This violates the requirement of statelessness on the parameter of Stream.map.
            – Andy Turner
            Dec 5 at 22:00




















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53635022%2fjava-8-lambda-filtering-based-on-condition-as-well-as-order%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?