0

I have list of objects. Assuming the structure of object is as follows.

class Test {
   Int id;
   String y;
}

Given a list 'testList' with four instances of Test (let's call them t1, t2, t3, t4).

Requirement is to obtain a list where only the items where the field 'y' is unique are retained. Each entry which has a duplicated value should be removed.

In the above case, assuming that t3 and t4 contains the same value of 'y', the result should be t1 and t2.

One solution is to first create a hash map:

  Map<String, List<Test>> yTestMap = new HashMap();

and use the field as key, adding each object that matches the key

Then loop through the HashMap entry set and where ever the value list contains more than one element remove those Test instances from the actual list.

for (List<Test> duplicateTestList : yTestMap.values())   
{                
     testList.removeAll(duplicateTestList);
}

Could you please suggest a more coincise way, maybe using Java 8 streams?

Luca Foppiano
  • 157
  • 12
Rob Wilkinson
  • 1,131
  • 5
  • 18
  • 34
  • You are trying to find distinct values in the list of `Test` objects by a key `y`. The original question should help you understand how you can achieve the same in one stream. The approach you have shared works as well though. – Naman Jul 01 '20 at 11:51
  • @Naman: Thanks for you comment. My question is not related to getting distinct values. In my case if any value repeats then it should not be included at all. – Rob Wilkinson Jul 01 '20 at 13:08
  • Without mutating the resulting list as you traverse the initial input, this wouldn't be possible in a single iteration then. Moreover, the pipeline, after you have created a grouped `Map` such as the one `Map> yTestMap` could just iterate only on the entries with `entry.getValue().size()==1` to provide a response. Also, forget efficient, the current `removeAll` would remove all the elements from the `testList`, so doesn't seem to be functionally correct either. – Naman Jul 01 '20 at 13:52
  • What I wanted to point would look similar to this `List testList; //input list List yTestResult = testList.stream() .collect(Collectors.groupingBy(Test::getY)) .entrySet().stream() .filter(e -> e.getValue().size() == 1) .flatMap(e -> e.getValue().stream()) .collect(Collectors.toList());` in the implementation, where the `groupingBy` produces the similar output as to your `Map` and the further pipeline only selects the unique objects. – Naman Jul 01 '20 at 14:00
  • This is a good question. I had the same problem. The title should be rephrased somehow. The answer from @Naman should be considered the correct answer IMHO. – Luca Foppiano Nov 05 '20 at 01:07

2 Answers2

0

you can use Set and Comparable. Before using it you should implements Comparable in Test.

import java.util.*;

public class Test implements Comparable<Test>{
 private int id;
 private String y;

 @Override
 public int compareTo(Test test){
   return y.compareTo(test.getY());
 }

 public void setY(String newY){
   y = newY;
 }

 public void setId(int newId){
   id = newId;
 }

 public String getY(){
   return y;
 }

 public String toString(){
  return "Id: " + id + "\nValue of Y: " + y + "\n";
 }
}

Duplicate values are not allowed in Set.

import java.util.*;

public class Main{

 public static void main(String arg[]){
    Set<Test> testList = new TreeSet<Test>();

    Test t0 = new Test();
    t0.setId(0);
    t0.setY("Y0");

    Test t1 = new Test();
    t1.setId(1);
    t1.setY("Y1");

    Test t2 = new Test();
    t2.setId(2);
    t2.setY("Y1");//here you can see same 'Y' value of t1 

    testList.add(t0);
    testList.add(t1);
    testList.add(t2);

    //t0 and t1 will be printed. t2 has duplicated Y-value so it's 
    // not included  
    testList.forEach(e -> System.out.println(e));
}

}

Jordy
  • 109
  • 5
0

Using streams you could do:

        List<Test> data = Arrays.asList(new Test(0, "a"),
                new Test(1, "b"),
                new Test(2, "c"),
                new Test(3, "c"));
        List<Test> answer = data.stream()
                .collect(Collectors.groupingBy(t -> t.y())) // Group by 'y'
                .entrySet().stream() // Stream of (key, value) pairs, value is a list of "Test"s
                .filter(kv -> kv.getValue().size() == 1) // Only those with one element
                .map(kv -> kv.getValue().get(0)) // Take the first element
                .collect(Collectors.toList());
        System.out.printf("In: %s\nOut: %s\n", data, answer);

Output is:

In: [Test[id=0, y=a], Test[id=1, y=b], Test[id=2, y=c], Test[id=3, y=c]]
Out: [Test[id=0, y=a], Test[id=1, y=b]]
Peter Hull
  • 6,683
  • 4
  • 39
  • 48