“Quotation, n: The act of repeating erroneously the words of another.”
― Ambrose Bierce, The Unabridged Devil’s Dictionary
Check out the earlier parts of this Java Collections Series: Introduction and Part 1.
Contents
1. Introduction
A Set is a container which does not contain duplicate elements. The item method equals() is used to determine whether the object is already present in the Set. At most one null element is allowed in the Set.
Commonly used concrete implementations of the Set interface include HashSet, LinkedHashSet and TreeSet, which are explained in detail below.
2. HashSet Example
A HashSet is the most common of the Set family. Use it when you do not need any ordering of the Set items. Specifically, it does not provide any ordering of items when iterating over the items.
Set<String> names = new HashSet<>(); names.addAll(Arrays.asList("Mary", "Annie", "Anna", "Margaret", "Helen", "Elsie", "Lucy", "Dorothy", "Mary", "Margaret")); names.forEach(System.out::println); // prints: Margaret Dorothy Elsie Annie Lucy Helen Mary Anna
Note that the order of items extracted from the Set demonstrates no predictable order. Also note the property of the Set: no duplicate elements; even though “Mary
” and “Margaret
” were inserted twice.
3. LinkedHashSet Example
Let us now look at the same example with a LinkedHashSet. When extracting using forEach() you can see that the order of items is the order they were inserted into the Set. The LinkedHashSet provides this capability by maintaining a linked list of items.
Set<String> names = new LinkedHashSet<>(); names.addAll(Arrays.asList("Mary", "Annie", "Anna", "Margaret", "Helen", "Elsie", "Lucy", "Dorothy", "Mary", "Margaret")); names.forEach(System.out::println); // prints: Mary Annie Anna Margaret Helen Elsie Lucy Dorothy
4. TreeSet Example
A TreeSet provides an implementation of a Set where the items are ordered with compareTo(). Alternatively, a Comparator implementation can be provided while constructing the TreeSet.
4.1. Natural Ordering
The following example illustrates the natural ordering of elements with the TreeSet, which for a String is dictionary ordering.
Set<String> names = new TreeSet<>(); names.addAll(Arrays.asList("Mary", "Annie", "Anna", "Margaret", "Helen", "Elsie", "Lucy", "Dorothy", "Mary", "Margaret")); names.forEach(System.out::println); // prints: Anna Annie Dorothy Elsie Helen Lucy Margaret Mary
Here is another example where we generate some random numbers in a stream and store it in a TreeSet. The numbers are ordered in the “natural ordering” which for numbers is ascending order.
Random random = new Random(); TreeSet<Long> linenos = random .longs(0, ntotal) .limit(nlines) .collect(TreeSet::new, Set::add, Set::addAll); System.out.println(); linenos.stream().forEach(System.out::println); // prints 372 575 1342 4362 5357 6436 12666 13882
4.2. Providing a Comparator
How about providing a Comparator to the TreeSet so the numbers are sorted in the reverse order?
Random random = new Random(); TreeSet<Long> linenos = random .longs(0, ntotal) .limit(nlines) .collect(() -> { return new TreeSet<Long>((x, y) -> y.compareTo(x)); }, Set::add, Set::addAll); System.out.println(); linenos.stream().forEach(System.out::println); // prints: 26295 25479 17203 14047 10805 9201 7161 1090
By the way, the Comparator is provided in the above example as a Lambda. With pre-java-8, you would provide something like this:
Comparator<Long> cmp = new Comparator<Long>() { @Override public int compare(Long o1,Long o2) { return o2.compareTo(o1); } }; ... .collect(() -> { return new TreeSet<Long>(cmp); }, Set::add, Set::addAll); ...
TreeSet stores the items added to it in a particular order — using the provided Comparator (or natural ordering if none provided). This means you can refer to an item relative to another item by using the navigational methods provided by TreeSet.
The class shown here is used in some of the following examples.
public class Player { private int year; private String teamId; private String leagueId; private String playerId; private int salary; public Player(int year, String teamId, String leagueId, String playerId, int salary) { this.year = year; this.teamId = teamId; this.leagueId = leagueId; this.playerId = playerId; this.salary = salary; } // standard getters and setters. public String toString() { StringBuilder sbuf = new StringBuilder(); sbuf.append(year).append(',') .append(teamId).append(',') .append(leagueId).append(',') .append(playerId).append(',') .append(salary); return sbuf.toString(); } };
5.1. Reversing a TreeSet
The method descendingSet() returns a view of the TreeSet with the items reversed.
TreeSet<Player> players = new TreeSet<>((x, y) -> { return Integer.compare(x.getYear(), y.getYear()); }); players.add(new Player(1986, "SLN", "NL", "lavalmi01",70000)); ... players.descendingSet().forEach(System.out::println); // prints: 1986,SLN,NL,lavalmi01,70000 1988,KCA,AL,wilsowi02,1383712 1989,CHA,AL,manrifr01,192500 1992,CHA,AL,karkoro01,650000 1995,KCA,AL,gubicma01,750000 1997,MON,NL,stankan01,220000 2000,BAL,AL,hairsje02,205000 2004,SFN,NL,willije02,308000 2005,MIL,NL,leeca01,8000000 2006,ATL,NL,jonesan01,13500000 2007,ARI,NL,valvejo01,2000000 2008,SDN,NL,ledezwi01,620000 2010,CHA,AL,lucydo01,400000 2011,TEX,AL,morelmi01,426000 2012,BOS,AL,doubrfe01,484000
5.2. Relative Ceiling and Floor
To obtain a ceiling item — the “smallest” item greater than or equal to the given — use ceiling().
Player p = new Player(1997, "MON" , "NL", "stankan01", 220000); ... System.out.println(players.ceiling(p)); // prints: 1997,MON,NL,stankan01,220000
The TreeSet also provides an analogous method floor() to extract the “largest” item greater than or equal to the given.
Player p = new Player(2008, "TOR", "AL", "coatsbu01", 392100); ... System.out.println(players.floor(p)); // prints: 2008,SDN,NL,ledezwi01,620000
The given element need not be present in the Set. It is used only for comparison.
Player p = new Player(2006, "COL", "NL", "hollima01", 500000); ... System.out.println(players.ceiling(p)); // prints: 2006,ATL,NL,jonesan01,13500000
5.3. Head Set and Tail Set
The head set is the set of all items in the Set less than the given item.
Player p = new Player(1997, "MON" , "NL", "stankan01", 220000); ... players.headSet(p).forEach(System.out::println); // prints: 1986,SLN,NL,lavalmi01,70000 1988,KCA,AL,wilsowi02,1383712 1989,CHA,AL,manrifr01,192500 1992,CHA,AL,karkoro01,650000 1995,KCA,AL,gubicma01,750000
And of course tailSet() returns all items greater than the given. Note that this includes the given element if it is present in the Set.
Player p = new Player(2007, "ARI", "NL", "valvejo01", 2000000); ... players.tailSet(p).forEach(System.out::println); // prints: 2007,ARI,NL,valvejo01,2000000 2008,SDN,NL,ledezwi01,620000 2010,CHA,AL,lucydo01,400000 2011,TEX,AL,morelmi01,426000 2012,BOS,AL,doubrfe01,484000
5.4. First and Last Item
Obtain the first and last items in the Set using first() and last(). These are the methods specified by SortedSet.
System.out.println("first: " + players.first()); System.out.println(" last: " + players.last()); // prints: first: 1986,SLN,NL,lavalmi01,70000 last: 2012,BOS,AL,doubrfe01,484000
Summary
Three common concrete implementations of the Set interface include a) HashSet which provides no ordering b) LinkedHashSet which maintains insertion order and c) TreeSet which orders items using a comparator while storing. In addition, the TreeSet provides methods for navigation, examples for which were presented.
See Also
- Java Collections Introduction gives an overall picture of the most important classes and interfaces.
- Java Collections – Part 1 covers iterating over collections and addition & removal of items.
- Java Collections – Sets explains Set operations and examples for HashSet, LinkedHashSet and TreeSet.
- Java Collections – ArrayList presents ArrayList-specific API methods.