ConcurrentHashMap vs ConcurrentSkipListMap - java

I want to compare performance between ConcurrentHashMap and ConcurrentSkipListMap. It's for studying purpose. Of corse the result depends on platform to platform. On my computer expectedly the reading test ConcurrentHashMap more productive then ConcurrentSkipListMap. But the writing test showed more performance ConcurrentSkipListMap. ConcurrentHashMap relies on a hash table, I think it should be more faster. Why is it happen?
package Concurrency;
import java.util.*;
import java.util.concurrent.*;
abstract class Task implements Callable<Long> {
protected static Map<Integer, String> map;
protected int nIteration;
protected static int index;
protected long startTime, endTime;
private static Random random = new Random();
private static char[] chars = "abcdefghijklmnopqrstuvwxyz".toCharArray();
public Task(Map<Integer, String> map, int nIteration) {
Task.map = map;
this.nIteration = nIteration;
}
protected static synchronized String getNextString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
char c = chars[random.nextInt(chars.length)];
sb.append(c);
}
sb.append(index);
return sb.toString();
}
protected static synchronized int getNextInt() { return index++; }
protected static synchronized int getPreviousInt() { return index--; }
protected static synchronized int getCurrentInt() { return index; } // It's for test purpose.
public abstract Long call();
}
class WriterTask extends Task {
public WriterTask(Map<Integer, String> map, int nIteration) { super(map, nIteration); }
public Long call() {
startTime = System.currentTimeMillis();
while(nIteration-- > 0) {
map.put(getNextInt(), getNextString());
}
endTime = System.currentTimeMillis();
return (endTime - startTime);
}
}
class ReaderTask extends Task {
public ReaderTask(Map<Integer,String> map, int nIteration) { super(map, nIteration); }
#Override
public Long call() {
startTime = System.currentTimeMillis();
while(nIteration-- > 0) {
map.remove(getPreviousInt());
}
endTime = System.currentTimeMillis();
return (endTime - startTime);
}
}
public class FourtyThree {
private static List<Future<Long>> result = new LinkedList<>();
private static Map<Integer, String> map;
//private static String mapName;
private static Map<String, Double> makeReport(
int nCycle, int nThreads, boolean isWriter , int nIteration)
throws InterruptedException, ExecutionException {
Long resultTime = 0L;
int numberLine = 0;
double resultAverage;
StringBuilder sb = new StringBuilder();
sb.append(map.getClass().getSimpleName());
sb.append(", Cycle:" + nCycle);
if(isWriter)
sb.append(", Test type:Writing");
else
sb.append(", Test type: Reading");
sb.append(", iteration:" + nIteration);
sb.append(", Threads:" +nThreads);
for(Future<Long> i : result) {
resultTime += i.get();
numberLine++;
}
resultAverage = (double)resultTime / (double)numberLine;
resultAverage = (double)Math.round(resultAverage * 100) / 100;
sb.append(", Average time:" + resultAverage + " milliseconds");
return Collections.singletonMap(sb.toString(), resultAverage);
}
private static void prepareReading(int nIteration) {
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.submit(new WriterTask(map, nIteration));
exec.shutdown();
}
public static Map<String, Double> test( Map<Integer, String> testMap,
int nCycle,
int nThreads,
boolean isWriter ,
int nIteration )
throws InterruptedException, ExecutionException {
map = testMap;
if (!isWriter)
prepareReading(nThreads * nIteration);
ExecutorService exec = Executors.newFixedThreadPool(nThreads);
List<Callable<Long>> tasks = new LinkedList<>();
for(int i = 0; i < nThreads; i++) {
if(isWriter)
tasks.add(new WriterTask(map, nIteration));
else
tasks.add(new ReaderTask(map, nIteration));
}
result = exec.invokeAll(tasks);
exec.shutdown();
map.clear();
return makeReport(nCycle, nThreads, isWriter , nIteration);
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Map<String, Double> results = new LinkedHashMap<String, Double>();
Collection<Double> resultTime = results.values();
double time = 0;
ConcurrentHashMap<Integer, String> map1 = new ConcurrentHashMap<>();
ConcurrentSkipListMap<Integer, String> map2 = new ConcurrentSkipListMap<>();
for(int i = 0; i < 5; i++) {
results.putAll(test(map1, i, 16, false, 1000));
}
for(Map.Entry<String, Double> entry : results.entrySet()) {
System.out.println(entry.getKey());
time += entry.getValue();
}
time = time / (double)resultTime.size();
time = Math.round(time * 100) / 100;
System.out.print("Average time for all cycles:" + time);
System.out.print(", Max time:" + Collections.max(resultTime));
System.out.print(", Min time:" + Collections.min(resultTime));
}
}
/* Short report:
*** Reading ***
ConcurrentHashMap, Cycle:4, Test type: Reading, iteration:1 000 000, Threads:2
Average time for all cycles:3530.0, Max time:6817.5, Min time:1625.0
ConcurrentSkipListMap, Cycle:4, Test type: Reading, iteration:1 000 000, Threads:2
Average time for all cycles:4716.0, Max time:9337.5, Min time:1294.0
ConcurrentHashMap, Cycle:4, Test type: Reading, iteration:100 000, Threads:16
Average time for all cycles:528.0, Max time:1064.06, Min time:355.25
ConcurrentSkipListMap, Cycle:4, Test type: Reading, iteration:100 000, Threads:16
Average time for all cycles:1081.0, Max time:1732.75, Min time:330.5
*** Writing ***
ConcurrentHashMap, Cycle:4, Test type:Writing, iteration:1 000 000, Threads:2
Average time for all cycles:12112.1, Max time:18261.5, Min time:9111.5
ConcurrentSkipListMap, Cycle:4, Test type:Writing, iteration:1 000 000, Threads:2
Average time for all cycles:11856.7, Max time:18143.0, Min time:8292.0
ConcurrentHashMap, Cycle:4, Test type:Writing, iteration:100 000, Threads:16
Average time for all cycles:9015.0, Max time:16461.75, Min time:5016.5
ConcurrentSkipListMap, Cycle:4, Test type:Writing, iteration:100 000, Threads:16
Average time for all cycles:8922.68, Max time:12383.31, Min time:6783.13
*/

Related

LongAdder vs Integer in Hash map for frequency map

I am constructing a frequency map in a single-threaded environment using a HashMap. The keys are the Strings whose frequencies needs to be tracked.
If I use HashMap<String, Integer>, each increment needs a new Integer.
Would a LongAdder perform better for this use case as I can simply call increment()? Some rudimentary testing showed that LongAdder does indeed perform slightly better but I am not sure why.
Testing to determine relative performance of incrementing integral types.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Function;
public class LongAdderTest {
public static void main(String[] args) {
new LongAdderTest().start();
}
public void start() {
int N = 100_000_000;
int warmup = 3;
String[] testNames = { "LongAdder", "Long", "Integer", "long",
"int", "Object", "int[]", "long[]" };
List<Function<Integer, Long>> tests = List.of(
this::longAdderTest, this::longWrapperTest,
this::integerWrapperTest, this::primitiveLongTest,
this::primitiveIntTest, this::objectTest,
this::intArrayTest, this::longArrayTest);
int i = 0;
for (Function<Integer, Long> test : tests) {
runTest(test, warmup, N, testNames[i++]);
}
}
public void runTest(Function<Integer, Long> test, int warmup,
int iterations, String testName) {
// warmup cycle
for (int i = 0; i < warmup; i++) {
long v = test.apply(iterations);
if (v != iterations) {
System.out
.println("Unexpected result - return = " + v);
}
}
long start = System.nanoTime();
long val = test.apply(iterations);
System.out.printf("%-10s : %12f %d%n", testName,
(System.nanoTime() - start) / 1_000_000., val);
}
public long longAdderTest(int iter) {
LongAdder val = new LongAdder();
Map<String, LongAdder> freq = new HashMap<>();
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.get("A").increment();
}
return freq.get("A").longValue();
}
public long longWrapperTest(int iter) {
Long val = 0L;
Map<String, Long> freq = new HashMap<>();
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.computeIfPresent("A", (k, v) -> v + 1);
}
return freq.get("A");
}
public long integerWrapperTest(int iter) {
Integer val = 0;
Map<String, Integer> freq = new HashMap<>();
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.computeIfPresent("A", (k, v) -> v + 1);
}
return freq.get("A");
}
public long primitiveLongTest(int iter) {
Map<String, Long> freq = new HashMap<>();
long val = 0;
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.computeIfPresent("A", (k, v) -> v + 1);
}
return freq.get("A");
}
public long primitiveIntTest(int iter) {
Map<String, Integer> freq = new HashMap<>();
int val = 0;
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.computeIfPresent("A", (k, v) -> v + 1);
}
return freq.get("A");
}
public long intArrayTest(int iter) {
Map<String, int[]> freq = new HashMap<>();
int[] val = { 0 };
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.get("A")[0] += 1;
}
return freq.get("A")[0];
}
public long longArrayTest(int iter) {
Map<String, long[]> freq = new HashMap<>();
long[] val = { 0L };
freq.put("A", val);
for (int i = 0; i < iter; i++) {
freq.get("A")[0] += 1;
}
return freq.get("A")[0];
}
public long objectTest(int iter) {
MyLongIncrement longObject = new MyLongIncrement(0);
Map<String, MyLongIncrement> freq = new HashMap<>();
freq.put("A", longObject);
for (int i = 0; i < iter; i++) {
freq.get("A").increment();
}
return freq.get("A").get();
}
static class MyLongIncrement {
long val;
public MyLongIncrement(long v) {
this.val = v;
}
public long get() {
return val;
}
public void increment() {
val += 1l;
}
}
}
Sample run.
LongAdder : 4166.724472 100000000
Long : 2929.021352 100000000
Integer : 5487.358323 100000000
long : 2993.538570 100000000
int : 2505.171838 100000000
Object : 1032.322116 100000000
int[] : 1132.710126 100000000
long[] : 1107.633331 100000000
Details
Using a Map brought the values closer. Imo, too close to make a definitive call.
But it would seem that incrementing in place with the last three types might be best since the value itself does not have to be updated in the map. Same for the LongAdder but the synchronization code could be a factor (or the test designer) for its less than stellar performance. But then, there could be many factors including my method of accessing the map value.
I think I'm done with this. Hope it shed some light on the issue.

Java Parallel search in a multidimensional array using Threads

The task is to find max and min values in a MxN matrix, and do it "in parallel" using Java Threads.
I was pretty surprised to find out that my parallelSearch() method runs much slower compared to the regularSearch() method. Both of them use the same brute-force algorithm, so even though you can probably solve this problem using a better algorithm, both methods should be on the even playing field.
I'm curious to find out why: is it because creating new Thread objects is a time-expensive task, or is it because I'm doing something totally wrong in my parallelSearch() method?
import java.util.Random;
public class Task {
static int max = -1;
static int min = 99;
static int[][] matrix;
static Random rnd = new Random();
static{
matrix = new int[4][1000];
Task t = new Task();
t.fill(matrix);
}
public static void main(String[] args) throws InterruptedException {
Task t = new Task();
long cur = System.currentTimeMillis();
System.out.println(t.regularSearch(matrix));
System.out.println("regular search took "+(System.currentTimeMillis() - cur)+" millis ");
cur = System.currentTimeMillis();
System.out.println(t.parallelSearch(matrix));
System.out.println("parallel search took "+(System.currentTimeMillis() - cur)+" millis ");
}
void fill(int[][] input){
for (int i = 0; i < input.length; i++) {
for (int i1 = 0; i1 < input[i].length; i1++) {
input[i][i1] = rnd.nextInt(200);
}
}
}
String regularSearch(int[][]input){
StringBuilder result = new StringBuilder();
for (int[] anInput : input) {
for (int anAnInput : anInput) {
if(anAnInput>max){
max = anAnInput;
}
if(anAnInput<min){
min = anAnInput;
}
}
}
String rslt = result.append("max: ").append(max).append(" min: ").append(min).toString();
max = -1;
min = 200; //doing this to have a fair comparison in main()
return rslt;
}
String parallelSearch(int[][] input) throws InterruptedException {
StringBuilder result = new StringBuilder();
for (int i = 0; i < input[0].length; i++) {
int x = i;
Thread t = new Thread(()->{
for (int[] anInput : input) {
if (anInput[x] > max) {
max = anInput[x];
}
if (anInput[x] < min) {
min = anInput[x];
}
}
});
t.start();
}
Thread.sleep(10);
result.append("max: ").append(max).append(" min: ").append(min);
return result.toString();
}
}
EDIT:
As was pointed out in the comments, my parallelSearch() was creating way more Threads than necessary, and I've rewritten it to better suit my task. This one actually runs faster than the regular one on big sizes. I've tested it against a 4x500000 matrix with random.nextInt(100000) bound and it runs as fast or even up to 10 seconds faster then the regularSearch()
String parallelSearch(int[][] input) throws InterruptedException {
StringBuilder result = new StringBuilder();
for (int i = 0; i < input.length; i++) {
int x = i;
Thread t = new Thread(()->{
for(int num:input[x]){
if(num>max){
max = num;
}
if(num<min){
min = num;
}
}
});
t.start();
}
Thread.sleep(10);
result.append("max: ").append(max).append(" min: ").append(min);
return result.toString();
}

How to design a test interface that times only some part of the code?

I want to time how long my code takes to perform on average over many test runs. In each test run, doWork() performs the work that I want to time. But I also want to checkWork() in each test run without it counting towards the time. I'm going to have many similar Exercise# classes, so I'd like to abstract the testing via a TestInterface. Is my current way a reasonable approach? Or is there a better design pattern / standard approach? Thanks in advance.
#FunctionalInterface
public interface TestInterface {
void test(final int NUM_TESTS);
}
public class TimeTests {
public static void test(TestInterface ti, final int NUM_TESTS, String testName) {
DecimalFormat df = new DecimalFormat("#.####");
long start = System.nanoTime();
ti.test(NUM_TESTS);
System.out.println("DEBUG: " + testName + " took "
+ df.format((System.nanoTime() - start) * 1.0 / NUM_TESTS)
+ " nanoseconds on average for " + NUM_TESTS + " tests");
}
}
public class Exercise1 {
private static final int NUM_TESTS = (int) Math.pow(10, 6);
private static void mainWork(List<Integer> A) {
// do stuff and time it
}
private static void checkWork(List<Integer> A) {
// do stuff but don't count it towards the time
}
public static void main(String[] args) {
TimeTests.test((NUM_TESTS_LOCAL) -> {
for (int i = 0; i < NUM_TESTS_LOCAL; ++i) {
List<Integer> A = new ArrayList<>();
// add random elements to A
mainWork(A);
checkWork(A);
}
}, NUM_TESTS, "Exercise1");
}
}
Okay, I think I managed to put together a decent framework (is this the right word?) for this task. If anybody could chime in to let me know if my approach is any good, I'd really appreciate it.
While my code seems to work fine for my use cases so far, I have a few questions:
In the interface definition of public interface CloneableTestInput<T extends CloneableTestInput<T>>, how is the type template <T extends CloneableTestInput<T> not a circular definition? I'm not sure I fully understand what that type template is saying.
Is there a way to make a generic CloneableList class that implements CloneableTestInput<List>? Currently, I need to make a separate implementation for each Collection type (e.g. ArrayList, LinkedList, ...). Similarly, is it possible to make a generic CloneableSet class that implements CloneableTestInput<Set>?
Thanks in advance :)
Testing Framework
Part I - An interface for test inputs
This allows TimeTests.java to work for generic input types.
public interface CloneableTestInput<T extends CloneableTestInput<T>> extends Cloneable {
T clone();
}
public class CloneableString implements CloneableTestInput<CloneableString> {
public String data;
public CloneableString() {}
public CloneableString(String input) { data = input; }
public CloneableString clone() { return new CloneableString(String.valueOf(data)); }
}
public class CloneableArrayList extends ArrayList implements CloneableTestInput<CloneableArrayList> {
public CloneableArrayList(ArrayList input) {
this.addAll(input);
}
#Override
public CloneableArrayList clone() {
return new CloneableArrayList(this);
}
}
Part II - An interface for timing tests
#FunctionalInterface
public interface TimeTestsInterface<outputType> {
void test(Callable<CloneableTestInput> formInput
, Function<CloneableTestInput, outputType> runAlgorithm
, Function<CloneableTestInput, outputType> getKnownOutput
, BiFunction<outputType, outputType, Boolean> checkResults
, final int NUM_TESTS, String testName);
}
public class TimeTests<outputType> implements TimeTestsInterface<outputType> {
public void test(Callable<CloneableTestInput> formInput
, Function<CloneableTestInput, outputType> runAlgorithm
, Function<CloneableTestInput, outputType> getKnownOutput
, BiFunction<outputType, outputType, Boolean> checkResults
, final int NUM_TESTS, String testName) {
try {
DecimalFormat df = new DecimalFormat("#.####");
long total = 0, start;
for (int i=0; i < NUM_TESTS; ++i) {
CloneableTestInput input = formInput.call();
CloneableTestInput orig_input = input.clone();
start = System.nanoTime();
outputType algorithmResult = runAlgorithm.apply(input);
total += System.nanoTime() - start;
outputType expectedResult = getKnownOutput.apply(orig_input);
assert(checkResults.apply(algorithmResult, expectedResult));
}
System.out.println("DEBUG: " + testName + " took "
+ df.format(total * 1.0 / NUM_TESTS)
+ " nanoseconds on average for " + NUM_TESTS + " tests");
} catch (Exception|AssertionError e) {
System.out.println(e.toString() + " - " + e.getMessage() + " - ");
e.printStackTrace();
}
}
}
Example Usages
Increment a BigInteger (uses CloneableArrayList)
/**
* Problem 6.2 from EPI
* Given an array A of digits encodiing a decimal number D,
* with MSD at A[0]. Update A to hold D + 1.
*/
public class PlusOne {
private static final int NUM_TESTS = (int) Math.pow(10, 5);
private static final int ARR_LENGTH = (int) Math.pow(10, 2);
private static ArrayList<Integer> plusOne(ArrayList<Integer> A) {
int n = A.size() - 1;
A.set(n, A.get(n) + 1);
for (int i = n; i > 0 && A.get(i) == 10; --i) {
A.set(i, 0);
A.set(i-1, A.get(i-1) + 1);
}
if (A.get(0) == 10) {
// Need additional digit up front as MSD
A.set(0,0);
A.add(0,1);
}
return A;
}
private static ArrayList<Integer> randArray(int len) {
ArrayList<Integer> A = new ArrayList<>();
if (len == 0) return A;
Random rgen = new Random();
A.add(rgen.nextInt(9) + 1);
--len;
while (len != 0) {
A.add(rgen.nextInt(10));
--len;
}
return A;
}
public static void main(String[] args) {
Callable<CloneableTestInput> formInput = () -> new CloneableArrayList(randArray(ARR_LENGTH));
Function<CloneableTestInput, ArrayList<Integer>> runAlgorithm =
(input) -> plusOne((ArrayList<Integer>) input);
Function<CloneableTestInput, ArrayList<Integer>> getKnownOutput =
(orig_input) -> {
BigInteger B = new BigInteger(Joiner.on("").join((ArrayList<Integer>) orig_input));
B = B.add(BigInteger.valueOf(1));
ArrayList<Integer> expectedOutput = new ArrayList<>();
while (B.compareTo(BigInteger.valueOf(0)) > 0) {
expectedOutput.add(0, B.mod(BigInteger.valueOf(10)).intValue());
B = B.divide(BigInteger.valueOf(10));
}
return expectedOutput;
};
BiFunction<ArrayList<Integer>, ArrayList<Integer>, Boolean> checkResults = List::equals;
TimeTests<ArrayList<Integer>> algTimer = new TimeTests<>();
algTimer.test(formInput, runAlgorithm, getKnownOutput, checkResults, NUM_TESTS, "PlusOne");
}
}
Can String be rearranged as a palindrome? (uses CloneableString)
public class CanStringBePalindrome {
private static final int INPUT_STRING_LENGTH = (int) Math.pow(10, 2);
private static final int NUM_TESTS = (int) Math.pow(10, 6);
private static boolean canFormPalindromeHash(final String s) {
Map<Character, Integer> charFreqs = new HashMap<>();
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (!charFreqs.containsKey(c))
charFreqs.put(c, 1);
else
charFreqs.put(c, charFreqs.get(c) + 1);
}
int oddFreqCount = 0;
for (Map.Entry<Character, Integer> entry : charFreqs.entrySet()) {
if ((entry.getValue() % 2) != 0 && (++oddFreqCount > 1))
return false;
}
return true;
}
private static boolean canFormPalindromeSorting(final String s) {
// TODO : find faster/simpler way of getting frequency counts
char[] a = s.toCharArray();
Arrays.sort(a);
int oddFreqCount = 0;
int numCurrChar =1;
for (int i = 1; i < a.length && oddFreqCount <= 1; ++i) {
if(a[i] != a[i-1]) {
if ((numCurrChar & 1) != 0)
++oddFreqCount;
numCurrChar = 1;
} else
++numCurrChar;
}
if ((numCurrChar & 1) != 0)
++oddFreqCount;
return oddFreqCount <= 1;
}
private static String randString(int len) {
StringBuilder sb = new StringBuilder();
Random rgen = new Random();
while (len-- > 0)
sb.append((char)(rgen.nextInt(26) + 'A'));
return sb.toString();
}
public static void main(String[] args) {
Callable<CloneableTestInput> formInput = () -> new CloneableString(randString(INPUT_STRING_LENGTH));
Function<CloneableTestInput, Boolean > runAlgorithm =
(input) -> canFormPalindromeHash(((CloneableString)input).data);
Function<CloneableTestInput, Boolean> getKnownOutput =
(orig_input) -> canFormPalindromeSorting(((CloneableString)orig_input).data);
BiFunction<Boolean, Boolean, Boolean> checkResults = Boolean::equals;
TimeTests<Boolean> algTimer = new TimeTests<>();
algTimer.test(formInput, runAlgorithm, getKnownOutput, checkResults
, NUM_TESTS, "CanStringBePalindrome");
}
}

Greedy Algorithm java in map

I'm working on emulator of ATM in java. The overall pattern in project is Command.
So I have 4 commands - getInfo, deposit,withdraw and exit.
I'm facing problems with an implementation of greedy algorithm in withdrawal method. It should return Map were first Integer is "denomination" and second Integer is "amount" left in ATM after we withdrew.
public Map<Integer, Integer> withdrawAmount(int expectedAmount)
So it takes expected amount as an argument and has to subtract it from ATM with the least possible amount of bills.
public class CurrencyManipulator
{
// denominations is a map where each denomination and it's quantity stored
private String currencyCode;
private Map<Integer, Integer> denominations = new HashMap<>();
public String getCurrencyCode()
{
return currencyCode;
}
public CurrencyManipulator(String currencyCode)
{
this.currencyCode = currencyCode;
}
public void addAmount(int denomination, int count)
{
if (denominations.containsKey(denomination))
{
denominations.put(denomination, denominations.get(count) + count);
} else
{
denominations.put(denomination, count);
}
}
public int getTotalAmount()
{
int sum = 0;
for (Map.Entry<Integer, Integer> pair : denominations.entrySet())
{
sum = pair.getKey() * pair.getValue();
}
return sum;
}
public boolean hasMoney()
{
return denominations.size() != 0;
}
public boolean isAmountAvailable(int expectedAmount)
{
return expectedAmount <= getTotalAmount();
}
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
}
}
So I need this method to return a map or throw exception if amount asked "expectedAmount" is higher then money available in ATM.
If we take $600 it could be - three bills: $500 + $50 + $50 OR $200 + $200 + $200, the preferred option is $500 + $50 + $50
Example, you have to give $600
The ATM has the following bill-count:
500 - 2
200 - 3
100 - 1
50 - 12
The result should be:
500 - 1
100 - 1
This what I came up with:
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
denominations.put(50,1);
denominations.put(500,1);
denominations.put(200,3);
HashMap<Integer, Integer> map = new HashMap<>();
TreeMap<Integer, Integer> sortedMap = new TreeMap<>(Collections.reverseOrder());
sortedMap.putAll(denominations);
ArrayList<Integer> bills = new ArrayList<>();
bills.addAll(sortedMap.keySet());
int num;
for (int i = 0; i < bills.size(); i++)
{
if (bills.get(i) <= expectedAmount)
{
num = expectedAmount / bills.get(i);
map.put(bills.get(i), num);
expectedAmount -= num * bills.get(i);
}
}
System.out.println(map);
return map;
}
It returns the map of needed bills and their quantity.
Now my question is..how do i compare it with the "denominations" map i have and subtract new map from it?
seems to be working code if someone ever needs it
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException
{
denominations.put(50,2);
denominations.put(500,1);
denominations.put(100,1);
HashMap<Integer, Integer> map = new HashMap<>();
TreeMap<Integer, Integer> sortedDenominations = new TreeMap<>(Collections.reverseOrder());
sortedDenominations.putAll(denominations);
ArrayList<Integer> bills = new ArrayList<>();
bills.addAll(sortedDenominations.keySet());
int num;
for (int i = 0; i < bills.size(); i++)
{
if (bills.get(i) <= expectedAmount)
{
num = expectedAmount / bills.get(i);
map.put(bills.get(i), num);
expectedAmount -= num * bills.get(i);
}
}
System.out.println(map);
for (Map.Entry<Integer,Integer> denominPresent:sortedDenominations.entrySet()){
int value;
for (Map.Entry<Integer,Integer> deominNeeded:map.entrySet()){
if(denominPresent.getKey().equals(deominNeeded.getKey())){
value = denominPresent.getValue()-deominNeeded.getValue();
if (value>=0) sortedDenominations.put(denominPresent.getKey(),value);
else throw new NotEnoughMoneyException();
}
}
}
System.out.println(sortedDenominations);
return sortedDenominations;
}
Another solution. Will work if you initialize the denominations variable using TreeMap
...
private Map<Integer, Integer> denominations = new TreeMap<>(Comparator.reverseOrder());
...
public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException {
final Map<Integer, Integer> map = new TreeMap<>(Comparator.reverseOrder());
// calculate denomination map to cash
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int denomination = entry.getKey();
if (denomination <= expectedAmount) {
int num = Math.min(expectedAmount / denomination, entry.getValue());
map.put(denomination, num);
expectedAmount -= num * denomination;
}
if (expectedAmount == 0) {
break;
}
}
if (expectedAmount != 0) {
throw new NotEnoughMoneyException();
}
map.forEach((key, value) -> {
denominations.compute(key, (denomination, count) -> {
return (count == value) ? null : count - value;
});
});
return map;
}

Division of a task to threads - multi threading

I want to generate pairs from a given large pool of numbers. I am using two for loops and threads. My function getAllPairs() in the code generates apairs with a given array of numbers.
I have an array of length 1000. With one thread, output time is nearly 15 sec. Now I want to use 5-6 threads and reduce this output time.I am stuck at dividing this task equally to five threads.If not threads,how to decrease the output time?
Solution with threads is appreciated since I put a lot of time learning multithreading. I would like to implement it.
import java.util.*;
class Pair {
public int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public String toString(){
return " ( " + x + " ," + y + " ) " ;
}
}
class selectPairs{
private int[] array;
private List<Pair> totalPairs ;
public selectPairs(int[] arr){
array = arr;
}
//set Method
public void settotalPairs(List<Pair> pieces){
totalPairs = pieces;
}
//get Method
public List<Pair> gettotalPairs(){
return totalPairs;
}
// Method to generate pairs
public List<Pair> getAllPairs() {
List<Pair> pairs = new ArrayList<Pair>();
int total = array.length;
for(int i=0; i < total; i++) {
int num1 = array[i];
for(int j=i+1; j < total; j++) {
int num2 = array[j];
pairs.add(new Pair(num1,num2));
}
}
return pairs;
}
}
// Thread class
class ThreadPairs extends Thread {
private Thread t;
selectPairs SP;
ThreadPairs(selectPairs sp){
SP = sp;
}
public void run() {
synchronized(SP) {
List<Pair> PAIRS = SP.getAllPairs();
SP.settotalPairs(PAIRS);
}
}
}
public class TestThread {
public static void main(String args[]) {
int[] a = new int[1000];
for (int i = 0; i < a.length; i++) {
a[i] = i ;
}
selectPairs ob = new selectPairs(a);
ThreadPairs T = new ThreadPairs( ob );
T.start();
while (true) {
try {
T.join();
break;
}
catch(Exception e){
}
}
List<Pair> Total = new ArrayList<Pair>() ;
List<Pair> Temp1 = ob.gettotalPairs();
Total.addAll(Temp1);
System.out.println(Total);
}
}
A solution with a thread-pool, a task split strategy and it collects all results:
public class SelectPairs {
private static final int NUM_THREADS = 8;
private int[] array;
public SelectPairs(int[] arr) {
array = arr;
}
// A splitting task strategy
public List<Pair> getPartialPairs(int threadIndex, int numThreads) {
List<Pair> pairs = new ArrayList<Pair>();
int total = array.length;
for (int i = threadIndex; i < total; i += numThreads) {
int num1 = array[i];
for (int j = i + 1; j < total; j++) {
int num2 = array[j];
pairs.add(new Pair(num1, num2));
}
}
return pairs;
}
// To use Callables or Runnables are better than extends a Thread.
public static class PartialPairsCall implements Callable<List<Pair>> {
private int thread;
private int totalThreads;
private SelectPairs selectPairs;
public PartialPairsCall(int thread, int totalThreads, SelectPairs selectPairs) {
this.thread = thread;
this.totalThreads = totalThreads;
this.selectPairs = selectPairs;
}
#Override
public List<Pair> call() throws Exception {
return selectPairs.getPartialPairs(thread, totalThreads);
}
}
public static void main(String[] args) throws Exception {
int[] a = new int[1000];
for (int i = 0; i < a.length; i++) {
a[i] = i;
}
SelectPairs sp = new SelectPairs(a);
// Create a thread pool
ExecutorService es = Executors.newFixedThreadPool(NUM_THREADS);
List<Future<List<Pair>>> futures = new ArrayList<>(NUM_THREADS);
// Submit task to every thread:
for (int i = 0; i < NUM_THREADS; i++) {
futures.add(es.submit(new PartialPairsCall(i, NUM_THREADS, sp)));
}
// Collect the results:
List<Pair> result = new ArrayList<>(a.length * (a.length - 1));
for (Future<List<Pair>> future : futures) {
result.addAll(future.get());
}
// Shutdown thread pool
es.shutdown();
System.out.println("result: " + result.size());
}
}
regarding the framework of multithreading, you can implement ThreadPoolExecutor as was suggested in a comment.
Regarding splitting the workload, it seems that the key is splitting the iteration on the array which is achievable if you give the Runnable task a start and end index to iterate over.

Categories

Resources