I have written a code snippet to read a sentence and print the words in the sentence along with their count of occurence.
Example:
String = Java is a language. java is easy and i like Java
Expected output :
Java =3, is=2 a=1, language=1, easy=1, and=1 i=1, like=1
I want to achieve it by using two nested for loops but I am missing something and the code is broken. Here is the snippet
package corejava;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class DuplicateStringOccurence {
public static void main(String[] args) {
// TODO Auto-generated method stub
String myString = " Java is a language. java is easy and i like Java";
String[] wordsInMySentence = getWords(myString);
Map<String, Integer> myMap = new HashMap<String, Integer>();
int countOfOccurence = 1;
// outloop i, innerlop j
for(int i=0;i<wordsInMySentence.length;i++) {
if (myMap.containsKey(wordsInMySentence[i])) {
countOfOccurence=1;
continue;
}
for(int j=i+1;j<wordsInMySentence.length;j++)
{
if (wordsInMySentence[i].equalsIgnoreCase(wordsInMySentence[j])) {
// match found
countOfOccurence++;
}
myMap.put(wordsInMySentence[i], countOfOccurence);
}
}
// print the duplicates and counts
for (Entry<String, Integer> entry : myMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue().toString());
}
}
private static String[] getWords(String myString) {
// TODO Auto-generated method stub
String[] wordsInMySentence = myString.replaceAll("[^a-zA-Z ]", "").toLowerCase().split("\\s+");
// create the array of words from the sentence
for (String s:wordsInMySentence) {
// System.out.println(s);
}
return wordsInMySentence;
}
}
I am not getting the expected output. I want to correct this piece of code . Can someone guide what is the mistake here?
Using java8 you can do like below :
First split you string by using regex ("[. ]+") and store that into List.
Then using Collectors.toMap ,
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper,BinaryOperator<U> mergeFunction)
It takes below three arguments :
KeyMapper - k -> k.toLowerCase()
ValueMapper - k -> 1
MergeFunction - Here Integer::sum
You can refer doc Collectors.toMap(keyMapper,valueMapper,mergeFunction)
public class WordCount {
public static void main(String[] args) {
String sentense= "Java is a language.java is easy and i like Java";
List<String> list = Stream.of(sentense).map(k -> k.split("[. ]+")).flatMap(Arrays::stream)
.collect(Collectors.toList());
Map<String, Integer> countMap= list.stream()
.collect(Collectors.toMap(k -> k.toLowerCase(), k -> 1, Integer::sum));
System.out.println(countMap);
// Output : {a=1, java=3, like=1, and=1, i=1, language=1, is=2, easy=1}
}
}
With Map data structure you need only one for loop. Here is one solution using basic for loop:
String sentense= "Java, is a language.java is easy and i like Java";
String[] words = sentense.split("\\W+");
Map<String, Integer> countMap = new HashMap<>();
for(String word : words) {
word = word.toLowerCase();
Integer count = countMap.containsKey(word)? countMap.get(word) + 1 : 1;
countMap.put(word, count);
}
System.out.println(countMap);
You can use the handy merge method to keep track of the sums
private static String[] getWords(String myString) {
return myString.trim().split("\\W+");
}
public static void main(String[] args) {
String myString = "\tJava is a language. java is easy and i like Java ";
Map<String, Integer> myMap = new HashMap();
for (String word : getWords(myString)) {
myMap.merge(word.toLowerCase(), 1, Integer::sum);
}
System.out.println(myMap);
}
I have written the following code for a Pair class in Java using Map.Entry. Does anyone have any recommendations for improving it? The most important criteria for me that the class needed to meet were that it would be easy to construct a Pair and that .equals would work the way I wanted it to (equality of the two objects in the Pair in the same order means equality of the Pairs). The code seems to function as intended and is not used in anything that is taxing my system's memory yet, but any optimizations would be appreciated.
import java.util.*;
public class Pair {
// Return a map entry (key-value pair) from the specified values
public static <T, U> Map.Entry<T, U> of(T first, U second) {
return new AbstractMap.SimpleEntry<>(first, second);
}
private Map.Entry entry;
public Pair (Object x, Object y) {
Set<Map.Entry> entries = new HashSet<>();
entries.add(Pair.of(x, y));
Object[] setArray = entries.toArray();
this.entry = (Map.Entry) setArray[0];
}
public Object getKey() {
return entry.getKey();
}
public Object getValue() {
return entry.getValue();
}
#Override
public boolean equals(Object o)
{
if (o instanceof Pair) {
Pair pair = (Pair) o;
return ( (entry.getKey().equals(pair.getKey())) && (entry.getValue().equals(pair.getValue())) );
}
return false;
}
#Override
public int hashCode() {
return Objects.hash(entry.getKey(), entry.getValue());
}
#Override
public String toString() {
return String.format( "(" +entry.getKey().toString() + ", " + entry.getValue().toString() + ")" );
}
}
I have been considering using Collections.singletonMap instead of Map.Entry.
So far, I have successfully tested my code using the following:
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Pair, Integer> testMap = new HashMap<>();
Pair onThrup = new Pair(1, 3);
System.out.println(onThrup); //Prints contents of Pair, not hash
testMap.put(onThrup, 7);
Pair unoTres = new Pair(1, 3);
System.out.println(onThrup.equals(unoTres));
System.out.println(testMap.containsKey(onThrup));
System.out.println(unoTres.hashCode() == onThrup.hashCode());
System.out.println(testMap.containsKey(unoTres)); // Should be, and is, true
}
}
Java 16 includes Record Classes:
public record Pair<K, V>(K key, V value) {
// intentionally empty
}
Creates a class with final fields, methods to retrieve key and value, equals, hashCode and toString methods.
Usage example:
var one = new Pair<Integer, String>(1, "one");
var two = new Pair(2, "two");
System.out.println(one); // Pair[key=1, value=one]
if (!one.equals(two))
System.out.printf("not same as Pair for %d%n", two.key());
not sure what is best, if available (java 16) I would use that
JLS 8.10
JEP 395
New to Java and was curious if there was a way to make the Value of a HashMap either a string or an ArrayList:
HashMap<String, HashMap<String, ArrayList<String>>> map = new HashMap<String, HashMap<String, ArrayList<String>>>();
map.putIfAbsent("238991", new HashMap<String, ArrayList<String>>());
map.get("238991").put("OrderID", new ArrayList<>());
map.get("238991").get("OrderID").add("1234H");
map.get("238991").get("OrderID").add("1233B");
map.get("238991").put("Name", new ArrayList<>());
map.get("238991").get("Name").add("Smith, John");
System.out.println(map.get("238991"));
System.out.println(map.get("238991").get("Name").get(0));
I would prefer to only add a String if I can for the Name rather than just accessing the first element of the list. Is this possible?
You should create a POJO, and use it as the HashMap value. It can contain all the data you need. Writing "pythonic" code in Java is just as bad, as doing it the other way around.
The answer is probably no.
I say "probably" because this:
System.out.println(map.get("238991").get("Name").toString());
will print:
[Smith, John]
This works because the ArrayList.toString() method will format the list. However, that is probably not what you want because:
there are square brackets around the name, and
the if you have more than one element in the list you will get all of them; e.g.
map.get("238991").get("Name").add("Jones, Barry");
[Smith, John, Jones, Barry]
You should work with POJOs.
Create a class that coveres your needs is much more feasible.
main.class
import java.util.ArrayList;
import java.util.HashMap;
public class OwnClass {
public static void main(String[] args) {
HashMap<String, Order> map = new HashMap<>();
ArrayList<String> orderItems = new ArrayList<>();
orderItems.add("1234H");
orderItems.add("1234B");
map.putIfAbsent("238991", new Order("Smith, John", orderItems));
map.get("238991").addOrder("1234J");
System.out.println(map);
}
}
Order.class
import java.util.ArrayList;
public class Order {
private String customer;
private ArrayList<String> items;
public Order(String string, ArrayList<String> orderItems) {
this.customer = string;
this.items = orderItems;
}
#Override
public String toString() {
return "Customer " + customer + " ordered " + items;
}
public void addOrder(String string) {
items.add(string);
}
}
Output:
{238991=Customer Smith, John ordered [1234H, 1234B, 1234J]}
import java.util.*;
public class MapReceivesListOrString{
public static void main(String []args){
boolean shouldAddList = true;
Map<String, Object> map = new HashMap<String, Object>(); //Creating a HashMap Polymorphically
List<String> list = new ArrayList<>();
list.add("1234H");
list.add("1233B");
String code1 = "some code one";
String code2 = "some code two";
if (shouldAddList) { // Your business logic whether to add a list or a string
map.put("OrderID", list);
} else {
map.put("code1", code1);
map.put("code2", code2);
}
for (Map.Entry<String, Object> mapValues : map.entrySet()) { // Iterate over many list's or many string's
Object value = mapValues.getValue();
if (value instanceof List) {
ArrayList myList = (ArrayList) value;
System.out.println("List value one: " + list.get(0));
System.out.println("List value two: " + list.get(1));
} else {
System.out.println("String value: " + value.toString());
}
}
}
}
Based on the existance of java generics we should define a specific type e.g. <String, ArrayList> rather than <String, Object> nonetheless it is a completely valid syntax.
I'm new to Java, so sorry if this is pretty obvious, but I can't quite understand how to work with 2 HashMaps inside each other
I have my main, where I want to add some words to a Map, and then, I want to read them:
public static void main(String[] args) {
Dicionario d = new Dicionario();
d.add("english", "BOOK", "Book");
d.add("french", "BOOK", "livre");
d.add("portuguese", "BOOK", "livro");
d.add("english", "YEAR", "year");
d.add("french", "YEAR", "an");
d.add("portuguese", "YEAR", "ano");
System.out.println(d);
}
This Map, has another Map inside him:
private Map<String, Map<String, String> > dic = new HashMap<>();
Then I add those words:
protected void add(String s1, String s2, String s3){
Map<String, String> m = new HashMap<>();
m.put(s2, s3);
dic.put(s1, m);
}
And redefine the function toString to read them, but only appears 1 value per key:
#Override
public String toString(){
String s= "";
for(Map.Entry<String, Map<String,String>> entry : dic.entrySet())
{
s += "\"" + entry.getKey() + "\": ";
for(Map.Entry<String, String> entry2 : dic.get(entry.getKey()).entrySet())
{
s+=entry2.getKey() + "->" + entry2.getValue() + "\t";
}
s+="\n";
}
return s;
}
Why is that? I am looking at this like if it was a bidimensional array, but with 2 values (key, value) in each position.
How can I do to show all the values that the keys from the first map have?
Thanks, and sorry for such a basic question.
You need to modify your add method to following
protected void add(String s1, String s2, String s3) {
Map<String, String> m = null;
m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
}
m.put(s2, s3);
dic.put(s1, m);
}
The problem is that in your add(String, String, String) method, you are instancing a new HashMap each time so you overwrite the previously instanced HashMap from a previous call.
You should update your method this way:
protected void add(String s1, String s2, String s3){
Map<String, String> m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
dic.put(s1, m);
}
m.put(s2, s3);
}
To avoid having to manage this by hand yourself, I suggest that you use Guava's Table data structure (more specifically HashBasedTable).
I want to have a map with duplicate keys.
I know there are many map implementations (Eclipse shows me about 50), so I bet there must be one that allows this. I know it's easy to write your own map that does this, but I would rather use some existing solution.
Maybe something in commons-collections or google-collections?
You are searching for a multimap, and indeed both commons-collections and Guava have several implementations for that. Multimaps allow for multiple keys by maintaining a collection of values per key, i.e. you can put a single object into the map, but you retrieve a collection.
If you can use Java 5, I would prefer Guava's Multimap as it is generics-aware.
We don't need to depend on the Google Collections external library. You can simply implement the following Map:
Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();
public static void main(String... arg) {
// Add data with duplicate keys
addValues("A", "a1");
addValues("A", "a2");
addValues("B", "b");
// View data.
Iterator it = hashMap.keySet().iterator();
ArrayList tempList = null;
while (it.hasNext()) {
String key = it.next().toString();
tempList = hashMap.get(key);
if (tempList != null) {
for (String value: tempList) {
System.out.println("Key : "+key+ " , Value : "+value);
}
}
}
}
private void addValues(String key, String value) {
ArrayList tempList = null;
if (hashMap.containsKey(key)) {
tempList = hashMap.get(key);
if(tempList == null)
tempList = new ArrayList();
tempList.add(value);
} else {
tempList = new ArrayList();
tempList.add(value);
}
hashMap.put(key,tempList);
}
Please make sure to fine tune the code.
Multimap<Integer, String> multimap = ArrayListMultimap.create();
multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");
multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");
multimap.put(3, "A");
System.out.println(multimap.get(1));
System.out.println(multimap.get(2));
System.out.println(multimap.get(3));
Output is:
[A,B,C,A]
[A,B,C]
[A]
Note: we need to import library files.
http://www.java2s.com/Code/Jar/g/Downloadgooglecollectionsjar.htm
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
or https://commons.apache.org/proper/commons-collections/download_collections.cgi
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
You could simply pass an array of values for the value in a regular HashMap, thus simulating duplicate keys, and it would be up to you to decide what data to use.
You may also just use a MultiMap, although I do not like the idea of duplicate keys myself.
If you want iterate about a list of key-value-pairs (as you wrote in the comment), then a List or an array should be better. First combine your keys and values:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Replace Class1 and Class2 with the types you want to use for keys and values.
Now you can put them into an array or a list and iterate over them:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
This problem can be solved with a list of map entry List<Map.Entry<K,V>>. We don't need to use neither external libraries nor new implementation of Map. A map entry can be created like this:
Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);
[June, 2021]
org.springframework.util.MultiValueMap
commons.apache.org - org.apache.commons.collections4
Learn from my mistakes...please don't implement this on your own.
Guava multimap is the way to go.
A common enhancement required in multimaps is to disallow duplicate keys-value pairs.
Implementing/changing this in a your implementation can be annoying.
In Guava its as simple as:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
No fancy libs required.
Maps are defined by a unique key, so dont bend them, use a list. Streams are mighty.
import java.util.AbstractMap.SimpleImmutableEntry;
List<SimpleImmutableEntry<String, String>> nameToLocationMap = Arrays.asList(
new SimpleImmutableEntry<>("A", "A1"),
new SimpleImmutableEntry<>("A", "A2"),
new SimpleImmutableEntry<>("B", "B1"),
new SimpleImmutableEntry<>("B", "B1"),
);
And thats it.
Usage examples:
List<String> allBsLocations = nameToLocationMap.stream()
.filter(x -> x.getKey().equals("B"))
.map(x -> x.getValue())
.collect(Collectors.toList());
nameToLocationMap.stream().forEach(x ->
do stuff with: x.getKey()...x.getValue()...
You can use a TreeMap with a custom Comparator in order to treat each key as unequal to the others. It would also preserve the insertion order in your map, just like a LinkedHashMap. So, the net result would be like a LinkedHashMap which allows duplicate keys!
This is a very simple implementation without the need of any third party dependencies or complications of MultiMaps.
import java.util.Map;
import java.util.TreeMap;
...
...
//Define a TreeMap with a custom Comparator
Map<Integer, String> map = new TreeMap<>((a, b) -> 1); // See notes 1 and 2
//Populate the map
map.put(1, "One");
map.put(3, "Three");
map.put(1, "One One");
map.put(7, "Seven");
map.put(2, "Two");
map.put(1, "One One One");
//Display the map entries:
map.entrySet().forEach(System.out::println);
//See note number 3 for the following:
Map<Integer, String> sortedTreeMap = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue,
(x, y) -> x, () -> new TreeMap<>((a, b) -> 1)
));
//Display the entries of this sorted TreeMap:
sortedTreeMap.entrySet().forEach(System.out::println);
...
Notes:
You can also use any positive integer in place of 1 in the comparator's definition here.
If you use any negative integer instead, then it will reverse the insertion order in your map.
If you also want to sort this map based on the keys (which is the default behavior of a TreeMap), then you may do this operation on the current map.
I had a slightly different variant of this issue: It was required to associate two different values with same key. Just posting it here in case it helps others, I have introduced a HashMap as the value:
/* #param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
#param innerMap: Key -> String (extIP), Value -> String
If the key exists, retrieve the stored HashMap innerMap
and put the constructed key, value pair
*/
if (frameTypeHash.containsKey(frameID)){
//Key exists, add the key/value to innerHashMap
HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
} else {
HashMap<String, String> innerMap = new HashMap<String, String>();
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
// This means the key doesn't exists, adding it for the first time
frameTypeHash.put(frameID, innerMap );
}
}
In the above code the key frameID is read from a input file's first string in each line, the value for frameTypeHash is constructed by splitting the remaining line and was stored as String object originally, over a period of time the file started having multiple lines (with different values) associated with same frameID key, so frameTypeHash was overwritten with last line as the value. I replaced the String object with another HashMap object as the value field, this helped in maintaining single key to different value mapping.
class DuplicateMap<K, V>
{
enum MapType
{
Hash,LinkedHash
}
int HashCode = 0;
Map<Key<K>,V> map = null;
DuplicateMap()
{
map = new HashMap<Key<K>,V>();
}
DuplicateMap( MapType maptype )
{
if ( maptype == MapType.Hash ) {
map = new HashMap<Key<K>,V>();
}
else if ( maptype == MapType.LinkedHash ) {
map = new LinkedHashMap<Key<K>,V>();
}
else
map = new HashMap<Key<K>,V>();
}
V put( K key, V value )
{
return map.put( new Key<K>( key , HashCode++ ), value );
}
void putAll( Map<K, V> map1 )
{
Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();
for ( Entry<K, V> entry : map1.entrySet() ) {
map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
}
map.putAll(map2);
}
Set<Entry<K, V>> entrySet()
{
Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
entry.add( new Entry<K, V>(){
private K Key = entry1.getKey().Key();
private V Value = entry1.getValue();
#Override
public K getKey() {
return Key;
}
#Override
public V getValue() {
return Value;
}
#Override
public V setValue(V value) {
return null;
}});
}
return entry;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{");
boolean FirstIteration = true;
for ( Entry<K, V> entry : entrySet() ) {
builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() ) );
FirstIteration = false;
}
builder.append("}");
return builder.toString();
}
class Key<K1>
{
K1 Key;
int HashCode;
public Key(K1 key, int hashCode) {
super();
Key = key;
HashCode = hashCode;
}
public K1 Key() {
return Key;
}
#Override
public String toString() {
return Key.toString() ;
}
#Override
public int hashCode() {
return HashCode;
}
}
1, Map<String, List<String>> map = new HashMap<>();
this verbose solution has multiple drawbacks and is prone to errors. It
implies that we need to instantiate a Collection for every value, check for
its presence before adding or removing a value, delete it manually when no
values are left, etcetera.
2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
java-map-duplicate-keys
what about such a MultiMap impl?
public class MultiMap<K, V> extends HashMap<K, Set<V>> {
private static final long serialVersionUID = 1L;
private Map<K, Set<V>> innerMap = new HashMap<>();
public Set<V> put(K key, V value) {
Set<V> valuesOld = this.innerMap.get(key);
HashSet<V> valuesNewTotal = new HashSet<>();
if (valuesOld != null) {
valuesNewTotal.addAll(valuesOld);
}
valuesNewTotal.add(value);
this.innerMap.put(key, valuesNewTotal);
return valuesOld;
}
public void putAll(K key, Set<V> values) {
for (V value : values) {
put(key, value);
}
}
#Override
public Set<V> put(K key, Set<V> value) {
Set<V> valuesOld = this.innerMap.get(key);
putAll(key, value);
return valuesOld;
}
#Override
public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
K key = valueEntry.getKey();
Set<V> value = valueEntry.getValue();
putAll(key, value);
}
}
#Override
public Set<V> putIfAbsent(K key, Set<V> value) {
Set<V> valueOld = this.innerMap.get(key);
if (valueOld == null) {
putAll(key, value);
}
return valueOld;
}
#Override
public Set<V> get(Object key) {
return this.innerMap.get(key);
}
#Override
etc. etc. override all public methods size(), clear() .....
}
Could you also explain the context for which you are trying to implement a map with duplicate keys? I am sure there could be a better solution. Maps are intended to keep unique keys for good reason. Though if you really wanted to do it; you can always extend the class write a simple custom map class which has a collision mitigation function and would enable you to keep multiple entries with same keys.
Note: You must implement collision mitigation function such that, colliding keys are converted to unique set "always". Something simple like, appending key with object hashcode or something?
just to be complete, Apache Commons Collections also has a MultiMap. The downside of course is that Apache Commons does not use Generics.
With a bit hack you can use HashSet with duplicate keys. WARNING: this is heavily HashSet implementation dependant.
class MultiKeyPair {
Object key;
Object value;
public MultiKeyPair(Object key, Object value) {
this.key = key;
this.value = value;
}
#Override
public int hashCode() {
return key.hashCode();
}
}
class MultiKeyList extends MultiKeyPair {
ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();
public MultiKeyList(Object key) {
super(key, null);
}
#Override
public boolean equals(Object obj) {
list.add((MultiKeyPair) obj);
return false;
}
}
public static void main(String[] args) {
HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
set.add(new MultiKeyPair("A","a1"));
set.add(new MultiKeyPair("A","a2"));
set.add(new MultiKeyPair("B","b1"));
set.add(new MultiKeyPair("A","a3"));
MultiKeyList o = new MultiKeyList("A");
set.contains(o);
for (MultiKeyPair pair : o.list) {
System.out.println(pair.value);
}
}
If there are duplicate keys then a key may correspond to more than one value. The obvious solution is to map the key to a list of these values.
For example in Python:
map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"] # It shows john and mike
print map["driver"][0] # It shows john
print map["driver"][1] # It shows mike
I used this:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
Just use simple Set with Pair. This Set will exclude pairs with the same key-value. Also you can iterate it.
val set = hashSetOf<Pair<String, String>>()
set.add(Pair("1", "a"))
set.add(Pair("1", "b"))
set.add(Pair("1", "b")) // Duplicate
set.add(Pair("2", "a"))
set.add(Pair("2", "b"))
set.forEach { pair -> println(pair) }
result: (1, a),(2, b),(1, b),(2, a)