How do I sort two ArrayLists and put them in a Map?

Viewed 141

My program calculates the digit sums of all values entered into a text file. The entered values and their according digit sums are stored in two seperate ArrayLists.

Both ArrayLists are combined into a LinkedHashMap in the end which should be ordered by the digit sum in descending order. If you enter multiple values with the same digit sum, it's supposed to order those (and only those) in descending order by their original value, not the digit sum this time (the rest stays the same as before).

How do I achieve this with Comparators?

My lists and the map:

String filePath = args[0];

LineNumberReader br = new LineNumberReader(new FileReader(filePath));
LinkedHashMap<BigInteger, BigInteger> unsortedMap = new LinkedHashMap<BigInteger, BigInteger>();
List<BigInteger> inputList = new ArrayList<>();
List<BigInteger> DSList = new ArrayList<>();

if(br.ready()){
     while (true) {
        String line = br.readLine();
        if (line == null) {
            break;
        }

        BigInteger input = new BigInteger(line);
        inputList.add(input);
        DSList.add(methods.digitSum(input));

    }
}

for(int i = 0; i < inputList.size(); i++){
    unsortedMap.put(inputList.get(i), DSList.get(i));
}

for(BigInteger key : unsortedMap.keySet()){
    System.out.println(new BigDecimal(key).toPlainString() + " (Digit Sum: " + unsortedMap.get(key) + (")"));
}

methods.digitSum:

public static BigInteger digitSum(BigInteger number) {

        String digits = number.toString();
        int sum = 0;

        for(int i = 0; i < digits.length(); i++) {
            int digit = (int) (digits.charAt(i) - '0');
            sum = sum + digit;
        }

        return BigInteger.valueOf(sum);

}

Output has to look like this:

x (digit sum: y)
x (digit sum: y)
...
x = entered value
y = digit sum of x

If you need any further information, feel free to ask.

3 Answers

Here is a solution with a simple class and a Comparator

class Values {
    BigInteger number;
    BigInteger digitSum;

    Values(BigInteger number, BigInteger sum) {
        this.number = number;
        this.digitSum = sum;
    }

    @Override
    public String toString() {
        return number + " (digit sun:" + digitSum + ")";
    }
}

And then create a list with this class

 List<Values> inputList = new ArrayList<>();

and add objects of Values to the list using the constructor when reading the file

For sorting you can create a Comparator object like this

Comparator<Values> compareSum = (Values v1, Values v2) -> {
   int result = v1.digitSum.compareTo(v2.digitSum);
   return result != 0 ? result : v1.number.compareTo(v2.number);           
};

and sort your list in descending order

inputList.sort(compareSum.reversed());

As there are duplicate keys with different values, you cannot use LinkedHashMap or any other Java Map implementations to store the same key with different value.

You can either create your custom Map or use Apache's Common Map Implementations. The following code shows a solution using MultiValueMap and a custom comparator that sorts by key and then by value in descending order:

  MultiValuedMap<BigInteger, BigInteger> unsortedMap = new ArrayListValuedHashMap<>();
  if(br.ready()){
     while (true) {
        String line = br.readLine();
        if (line == null) {
            break;
        }
        //Use a single map to put both values
        unsortedMap.put(new BigInteger(line), new BigInteger(methods.digitSum(line)));
    }
  }

  //Do the sorting using custom comparator

  List<Map.Entry<BigInteger, BigInteger>> list = new LinkedList<>(unsortedMap.entries());
  // Sort the list
  Collections.sort(list, new Comparator<Map.Entry<BigInteger, BigInteger> >() {
     public int compare(Map.Entry<BigInteger, BigInteger> o1,
                               Map.Entry<BigInteger, BigInteger> o2) {
        int c = o1.getKey().compareTo(o2.getKey());
        if (c==0){
           c = o1.getValue().compareTo(o2.getValue());
        }
        return c * -1; //descending
     }
  });

  //Place the sorted List into another Map that retains the insert order
  Multimap<BigInteger, BigInteger> sortedMap = LinkedHashMultimap.create();
  for (Map.Entry<BigInteger, BigInteger> entry : list) {
    sortedMap.put(entry.getKey(), entry.getValue());
  }

  //Print
  for(BigInteger key : sortedMap.keySet()){
    for (BigInteger bi: sortedMap.get(key)) {
       System.out.println("x: " + new BigDecimal(key).toPlainString() + " (Digit Sum: " + bi + (")"));
    }
  }

To use the above Maps you should include the apache commons collections dependency

 <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-collections4</artifactId>
      <version>4.4</version>
  </dependency>

You can refer to below code to sort your map by value in descending order (in case of same values, sort in descending order of keys). In your case, the keys of the map (passed in parameter) are the inputs from text file and respective values in map are the digit sum.

public static LinkedHashMap<BigInteger, BigInteger> sortByValue(LinkedHashMap<BigInteger, BigInteger> hm)
{
    List<Map.Entry<BigInteger, BigInteger> > list =
            new LinkedList<Map.Entry<BigInteger, BigInteger> >(hm.entrySet());

    Collections.sort(list, new Comparator<Map.Entry<BigInteger, BigInteger> >() {
        public int compare(Map.Entry<BigInteger, BigInteger> o1,
                           Map.Entry<BigInteger, BigInteger> o2)
        {
            // To handle same value case
            if(o1.getValue().compareTo(o2.getValue()) == 0) {
                return (o2.getKey()).compareTo(o1.getKey());
            }

            return (o2.getValue()).compareTo(o1.getValue());
        }
    });

    LinkedHashMap<BigInteger, BigInteger> temp = new LinkedHashMap<BigInteger, BigInteger>();
    for (Map.Entry<BigInteger, BigInteger> aa : list) {
        temp.put(aa.getKey(), aa.getValue());
    }
    return temp;
}

The compareTo method is called on object o2 rather than object o1 to get the result in descending order. As mentioned in comment of above code snippet, the if condition will handle your requirement of comparing the keys and sort in descending order of keys when the values are same.

The list of Entry objects is sorted as as per your requirement and these sorted Entry objects are added to LinkedHashMap to maintain the insertion order into the map.

Related