vale4674
vale4674

Reputation: 4271

How to intersect multiple sets?

I have this list:

private List<Set<Address>> scanList;

So my list contains multiple scans as you can see. After each scan I add new set into the list.

After all scans are finished I would like to take only the addresses that occur in every set and put it into:

private List<Address> addresses;

Does something like this already exists in Set/TreeSet/HashSet?

EDIT: after answers, retainAll() is the right method. Thank you. Here is the source:

Set<Address> addressCross = scanList.get(0);
for (int i = 1; i < scanList.size(); i++) {
    addressCross.retainAll(scanList.get(i));
}   
for (Address address : addressCross) {
    addresses.add(address);
}

Upvotes: 26

Views: 24927

Answers (4)

Chris
Chris

Reputation: 4648

There's another nice solution over here: https://stackoverflow.com/a/38266681/349169

Set<Address> intersection = scanList.stream()
    .skip(1) // optional
    .collect(()->new HashSet<>(scanList.get(0)), Set::retainAll, Set::retainAll);

For example, in modern Java:

record Address( String street ) { }
List < Set < Address > > listOfSetsOfAddresses =
        List.of (  // Only the tree-species are in common, Oak & Maple.
                Set.of ( new Address ( "Oak" ) , new Address ( "Maple" ) , new Address ( "Broad" ) ) ,
                Set.of ( new Address ( "Main" ) , new Address ( "Maple" ) , new Address ( "Oak" ) ) ,
                Set.of ( new Address ( "Oak" ) , new Address ( "Meridian" ) , new Address ( "Maple" ) )
        );
Set < Address > intersection =
        listOfSetsOfAddresses
                .stream ( )
                .skip ( 1 )
                .collect (
                        ( ) -> new HashSet <> ( listOfSetsOfAddresses.getFirst ( ) ) ,
                        Set :: retainAll ,
                        Set :: retainAll
                );

System.out.println ( "intersection.toString() = " + intersection );

intersection.toString() = [Address[street=Oak], Address[street=Maple]]

Upvotes: 1

Lavir the Whiolet
Lavir the Whiolet

Reputation: 1016

retainAll

Use Set#retainAll or List#retainAll() to get an intersection.

To quote the Javadoc:

Retains only the elements in this list that are contained in the specified collection (optional operation). In other words, removes from this list all of its elements that are not contained in the specified collection.

For example code using streams, see Answer by Chris.

Upvotes: 13

ColinD
ColinD

Reputation: 110046

With Guava, you could do it like this:

Set<Address> intersection = scanList.get(0);
for (Set<Address> scan : scanList.subList(1, scanList.size())) {
  intersection = Sets.intersection(intersection, scan);
}
List<Address> addresses = Lists.newArrayList(intersection);

This creates a view of the intersection of all the sets in the scanList and then copies the addresses in the intersection into a List. You would need to ensure your scanList has at least one element in it, of course.

Upvotes: 8

Jack
Jack

Reputation: 133567

you can use retainAll(Collection<?> c), check it out here

A side note: that operation is called intersection.

To convert then it to a List you can use the method addAll(Collection<? extends E> c) which should work between all kinds of containers.

eg:

ArrayList<Address> list = new ArrayList<Address>();
list.addAll(yourSet);

Upvotes: 16

Related Questions