java 8 groupby multiple attributes - java

I have list of Collections elements as below. I wanted to groupBy multiple fields like productCode and then for productpurchase type.
class Product{
private String productCode;
// this is ENUM with 2 possible values "ONLINE" or "INSTORE"
private String productPurchaseType;
private String productCost;
...
}
The possible output should be like
ROW1::ProductCode1, Count of ONLINE,Count of INSTORE,Min, Max
ROW2::ProductCode2, Count of ONLINE,Count of INSTORE, Min, Max
i used below piece of code, but it doesn't give the count of ONLINE and INSTORE
void groupByMerchantMCCCodeZIP(List<Product> productList) {
Map<String, Map<String, List<Product>>> output = transactionDataList.stream()
.collect(Collectors.groupingBy(Product::getProductCode,
Collectors.groupingBy(Product::productPurchaseType)));
System.out.println(output);
}
Can someone who worked on java 8 groupby let me know best way to do this?
Thanks!

To aggregate multiple values, you should write your own Collector, for best performance.
Easiest way to write a Collector is to call the Collector.of() method, in combination with a result collector class. Below is an example.
First, I redefined the Product to have better field types:
class Product {
public enum PurchaseType { ONLINE, INSTORE }
private final String code;
private final PurchaseType purchaseType;
private final BigDecimal cost;
public Product(String code, PurchaseType purchaseType, String cost) {
this.code = code;
this.purchaseType = purchaseType;
this.cost = new BigDecimal(cost);
}
public String getCode() {
return this.code;
}
public PurchaseType getPurchaseType() {
return this.purchaseType;
}
public BigDecimal getCost() {
return this.cost;
}
}
Then the result collector class:
class ProductResult {
private int onlineCount;
private int instoreCount;
private BigDecimal minCost;
private BigDecimal maxCost;
public void add(Product product) {
if (product.getPurchaseType() == Product.PurchaseType.ONLINE)
this.onlineCount++;
else if (product.getPurchaseType() == Product.PurchaseType.INSTORE)
this.instoreCount++;
if (this.minCost == null || product.getCost().compareTo(this.minCost) < 0)
this.minCost = product.getCost();
if (this.maxCost == null || product.getCost().compareTo(this.maxCost) > 0)
this.maxCost = product.getCost();
}
public ProductResult merge(ProductResult that) {
this.onlineCount += that.onlineCount;
this.instoreCount += that.instoreCount;
if (this.minCost == null || that.minCost.compareTo(this.minCost) < 0)
this.minCost = that.minCost;
if (this.maxCost == null || that.maxCost.compareTo(this.maxCost) > 0)
this.maxCost = that.maxCost;
return this;
}
#Override
public String toString() {
return "[online: " + this.onlineCount +
", instore: " + this.instoreCount +
", min: " + this.minCost +
", max: " + this.maxCost + "]";
}
public int getOnlineCount() {
return this.onlineCount;
}
public int getInstoreCount() {
return this.instoreCount;
}
public BigDecimal getMinCost() {
return this.minCost;
}
public BigDecimal getMaxCost() {
return this.maxCost;
}
}
Demo
List<Product> productList = Arrays.asList(
new Product("MILK", Product.PurchaseType.ONLINE, "3.99"),
new Product("MILK", Product.PurchaseType.ONLINE, "3.99"),
new Product("MILK", Product.PurchaseType.INSTORE, "4.95"),
new Product("BREAD", Product.PurchaseType.INSTORE, "7.48")
);
Map<String, ProductResult> result = productList.stream()
.collect(Collectors.groupingBy(Product::getCode,
Collector.of(ProductResult::new,
ProductResult::add,
ProductResult::merge,
Characteristics.UNORDERED)));
result.entrySet().forEach(System.out::println);
Output
BREAD=[online: 0, instore: 1, min: 7.48, max: 7.48]
MILK=[online: 2, instore: 1, min: 3.99, max: 4.95]

Related

How can I create a Java algorithm that takes any number of players and pair them up any number of times uniquely?

I am looking to create an algorithm in Java that can take any number of "players" and group them up a specified number of times each. However, two pairs cannot be the same. So, if we are supplied 9 players (dubbed 0, 1, 2, etc) by the user and each player should be paired 3 times, that means that this algorithm needs to be able to generate a list of pairs where each player is paired 3 times.
So 4 players being paired two times could be: {{0, 1}, {2, 3}, {0, 2}, {1, 3}}.
Obviously, it can be impossible in some scenarios (like 4 players being uniquely paired 20 times), but I have input restrictions to combat that.
{0, 1} and {1, 0} are equal pairs. The order of the numbers does not matter, they are not unique.
The preferable way for input is just given two numbers (number of players, number of pairs per players) and the preferable way for the output to be given is in a two dimensional array of integers (each player being dubbed by an integer), like I gave an example of.
Does anyone have any ideas on how to do this? Pseudo-code, actual code, any ideas are welcome. Thanks!
I think your question is valid and interesting to solve in code.
This is why I coded that whole thing.
There's one downside to my solution, or rather: the problem.
In certain situations, some players can have many matches between them, while others have little. So in the end, some players might not get matched properly.
In this case, you'd need a mathematical trick, or a backtracking algorithm, that steps back on parts of the solution and tries (brute-forces) other combinations. My algorithm has neither, but it indicates Exceptions and validity.
Also check the comments in the code.
package snippet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
public class BadPairingStuff6 {
static class Player {
public final int mID;
private final BadPairingStuff6 mParentLogic;
public int mMatches;
public Player(final int pID, final BadPairingStuff6 pBadPairingStuff5) {
mID = pID;
mParentLogic = pBadPairingStuff5;
}
#Override public int hashCode() {
return mID;
}
#Override public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Player other = (Player) obj;
if (mID != other.mID) return false;
return true;
}
#Override public String toString() {
return "Player[" + mID + "]";
}
public void incMatches() {
++mMatches;
}
public int getMatches() {
return mMatches;
}
public boolean canPlayAnotherMatch() {
return getMatches() < mParentLogic.mPairingsAllowed;
}
}
static class Pairing {
public final Player mPlayer1;
public final Player mPlayer2;
public Pairing(final Player pPlayer1, final Player pPlayer2) {
if (pPlayer1.mID < pPlayer2.mID) {
mPlayer1 = pPlayer1;
mPlayer2 = pPlayer2;
} else {
mPlayer1 = pPlayer2;
mPlayer2 = pPlayer1;
}
}
#Override public String toString() {
return mPlayer1 + "+" + mPlayer2;
}
#Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mPlayer1.mID;
result = prime * result + mPlayer2.mID;
return result;
}
#Override public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Pairing other = (Pairing) obj;
if (mPlayer1 != other.mPlayer1) return false;
if (mPlayer2 != other.mPlayer2) return false;
return true;
}
}
static class PartnerMap {
private final HashMap<Player, ArrayList<Player>> mMap = new HashMap<>();
public PartnerMap(final Iterable<Player> pPlayers) {
for (final Player player : pPlayers) {
final ArrayList<Player> partners = new ArrayList<>();
for (final Player partner : pPlayers) {
if (player != partner) partners.add(partner);
}
mMap.put(player, partners);
}
}
public Player getPartner(final Player pPlayer) {
final ArrayList<Player> possiblePartners = mMap.get(pPlayer);
if (possiblePartners.size() < 1) throw new NotEnoughPartnersException(pPlayer);
return possiblePartners.get((int) (Math.random() * possiblePartners.size()));
}
public void removePartners(final Player pPlayer, final Player pPartner) {
System.out.println("\t\tBadPairingStuff5.PartnerMap.removePartners(" + pPlayer + ", " + pPartner + ")");
System.out.println("\t\t\tRemoving for " + pPlayer);
System.out.println("\t\t\t\tBEFORE: " + toString(mMap.get(pPlayer)));
mMap.get(pPlayer).remove(pPartner);
System.out.println("\t\t\t\tAFTER: " + toString(mMap.get(pPlayer)));
System.out.println("\t\t\tRemoving for " + pPartner);
System.out.println("\t\t\t\tBEFORE: " + toString(mMap.get(pPartner)));
mMap.get(pPartner).remove(pPlayer);
System.out.println("\t\t\t\tAFTER: " + toString(mMap.get(pPartner)));
}
static String toString(final Iterable<Player> pPlayers) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
for (final Player player : pPlayers) {
sb.append(player.mID + " ");
}
sb.append("]");
return sb.toString();
}
public void removePlayerCompletely(final Player pPlayer) {
System.out.println("\t\t\tBadPairingStuff5.PartnerMap.removePlayerCompletely(" + pPlayer + ")");
for (final ArrayList<Player> partnerMap : mMap.values()) {
partnerMap.remove(pPlayer);
}
mMap.get(pPlayer).clear();
}
public void print() {
System.out.println("Partner Map");
for (final Entry<Player, ArrayList<Player>> e : mMap.entrySet()) {
System.out.println("\t" + e.getKey());
for (final Player v : e.getValue()) {
System.out.println("\t\t" + v);
}
}
}
}
public static class NotEnoughPartnersException extends IllegalStateException {
private static final long serialVersionUID = -7249807214069096317L;
private final Player mPlayer;
public NotEnoughPartnersException(final Player pPlayer) {
super("Not enough partners available for " + pPlayer + "!");
mPlayer = pPlayer;
}
public Player getPlayer() {
return mPlayer;
}
}
static class PairingResult {
public final ArrayList<Pairing> mCreatedPairings;
public final ArrayList<Exception> mExceptions;
public PairingResult(final ArrayList<Pairing> pCreatedPairings, final ArrayList<Exception> pExceptions) {
mCreatedPairings = pCreatedPairings;
mExceptions = pExceptions;
}
public boolean isValid() {
return mExceptions.size() < 1;
}
}
public static void main(final String[] args) {
final int players = 10;
final int pairingsAllowed = 4;
final PairingResult result = new BadPairingStuff6(players, pairingsAllowed).createPairings();
System.out.println("All pairings:");
final HashMap<Long, Long> playCounter = new HashMap<>();
for (final Pairing p : result.mCreatedPairings) {
System.out.println("\t" + p);
{
final Long oldCount = playCounter.get(Long.valueOf(p.mPlayer1.mID));
playCounter.put(Long.valueOf(p.mPlayer1.mID), Long.valueOf(oldCount == null ? 1 : (oldCount.longValue() + 1)));
}
{
final Long oldCount = playCounter.get(Long.valueOf(p.mPlayer2.mID));
playCounter.put(Long.valueOf(p.mPlayer2.mID), Long.valueOf(oldCount == null ? 1 : (oldCount.longValue() + 1)));
}
}
System.out.println("Pairings per Player: ");
for (final Entry<Long, Long> e : playCounter.entrySet()) {
System.out.println("\t" + e.getKey() + " -> " + e.getValue());
}
System.out.println("Exceptions:");
System.out.flush();
sleep();
for (final Exception e : result.mExceptions) {
e.printStackTrace();
}
System.err.flush();
sleep();
System.out.println("Valid result: " + result.isValid());
System.out.println("All done.");
}
/*
* OBJECT
*/
final int mPairingsAllowed;
private final ArrayList<Player> mPlayers = new ArrayList<>();
public BadPairingStuff6(final int pPlayersCount, final int pPairingsAllowed) {
mPairingsAllowed = pPairingsAllowed;
// create players
for (int i = 0; i < pPlayersCount; i++) {
mPlayers.add(new Player(i, this));
}
}
public PairingResult createPairings() {
final ArrayList<Pairing> createdPairings = new ArrayList<>();
final ArrayList<Exception> exceptions = new ArrayList<>();
final PartnerMap possiblePairings = new PartnerMap(mPlayers);
final HashSet<Player> playersToHandle = new HashSet<>(mPlayers);
while (!playersToHandle.isEmpty()) {
final ArrayList<Player> removePlayersPerRound = new ArrayList<>();
for (final Player player : playersToHandle) {
if (!player.canPlayAnotherMatch()) {
possiblePairings.removePlayerCompletely(player);
removePlayersPerRound.add(player);
continue;
}
try {
System.out.println("Creating matches for " + player + " (" + player.getMatches() + ")");
final Player partner = possiblePairings.getPartner(player);
if (!partner.canPlayAnotherMatch()) continue;
final Pairing newPairing = new Pairing(player, partner);
if (createdPairings.contains(newPairing)) System.out.println("WARNING! Double hit for " + newPairing);
createdPairings.add(newPairing);
possiblePairings.removePartners(player, partner);
player.incMatches();
partner.incMatches();
System.out.println("\tMatched with " + partner);
if (!partner.canPlayAnotherMatch()) {
possiblePairings.removePlayerCompletely(partner);
removePlayersPerRound.add(partner);
}
} catch (final NotEnoughPartnersException e) {
// the flushes and sleeps are only a cheap shot to keep System.out and System.err outputs in somewhat chronological order.
// this is for proof/debug/answer only, and should NOT be used in production!
System.out.flush();
sleep();
e.printStackTrace();
// throw e; // if you want to abort early
removePlayersPerRound.add(e.getPlayer());
exceptions.add(e);
System.err.flush();
sleep();
}
}
playersToHandle.removeAll(removePlayersPerRound);
}
possiblePairings.print();
return new PairingResult(createdPairings, exceptions);
}
// the sleeps are only a cheap shot to keep System.out and System.err outputs in somewhat chronological order.
// this is for proof/answer only, and should NOT be used in production
static void sleep(final long pMilliSec) {
try {
Thread.sleep(pMilliSec);
} catch (final InterruptedException e1) { /* */ }
}
static void sleep() {
sleep(100);
}
}
I use lots of inner static classes. This is for demonstration purposes only.
If you want to actually use those classes, put each of them into its separate file (remove the "static class", and add a "public class" where it's missing).
Also note that this complexity is needed for random assignments. If the algorithm could always churn out the same combinations, it would be about 1/10th of the code.

Method returning null no matter what

I have two classes I have posted here (plus a computer class that the objects are being created from).
When I try to use my findSys method, my method returns "null" no matter what. I am trying to compare the "search" variable the user inputs as a findSys parameter against null, and if it's null, it should output the message I have underneath the "else" clause. But instead, its just returning null no matter what. Stuck here.
import java.util.Scanner;
public class SystemTester {
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
String search;
ComputerStore cpu1 = new ComputerStore();
cpu1.add("Pentium II", 32, "2080", "Asus 370", "Corsair", 5, 123, 5);
cpu1.add("Pentium I", 16, "Nvidia 1080", "Asus 270", "CoolerMaster1", 5, 123, 5);
cpu1.add("Pentium III", 4, "GTX 1060", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("AMD", 4, "GTX 980", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("AMD Ryzen", 4, "GTX 680", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("Core I5", 4, "GTX 1080ti", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("Core I7", 4, "GTX 1060 SLI", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("Core I9", 4, "GTX 780", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("AMD Ryzen 2", 4, "Voodoo2", "Gigabyte", "Corssair 2", 5, 123, 5);
cpu1.add("I7 5820k", 4, "Voodoo1", "Gigabyte", "Corssair 2", 5, 123, 5);
ComputerStore cpu2 = new ComputerStore();
cpu2.add("Haswell", 64, "Nvidia 1080", "Aztek", "Corsair", 3.5, 455, 5.5);
System.out.println("Please enter a CPU to search for (Press q to quit)");
search = scan.nextLine();
while (!"q".equals(search)) {
if (search != null) {
System.out.println(cpu1.findSys(search));
}
else {
if (search.equals(null))
System.out.println("test");
}
System.out.println("Please enter a CPU to search for (Press q to quit)");
search = scan.nextLine();
}
}
}
public class ComputerStore {
private Computer[] systems;
private int sysNumbers;
public ComputerStore() {
systems = new Computer[200];
sysNumbers = 0;
}
public void add(String c, int r, String g, String m, String p, double co, int sn, double d) {
systems[sysNumbers++] = new Computer(c, r, g, m, p, co, sn, d);
}
public String toString() {
String result = "";
for (int i = 0; i < sysNumbers; i++)
result += systems[i].toString() + "\n";
return result;
}
public String findSys(String c) {
for (int i = 0; i < sysNumbers; i++) {
if (systems[i] != null && systems[i].getCpu().equals(c))
return systems[i].getMotherboard();
}
return null;
}
}
//
//This program will create Computer objects with different data members
//and will also upgrade those data members based on setters. This program
//also has a depreciation function and upgrade function. This (Computer) is the class
//and the SystemBuilder class is the class used for creating the objects.
public class Computer {
// Data Members - These belong to the class and are private.
// They all go to new objects.
private String cpu;
private int ram;
private String gpu;
private String motherboard;
private String psu;
private double cost;
private int serialnumber;
private double depreciation;
// Initial constructor with no arguments
Computer() {
cpu = "";
ram = 0;
gpu = "";
motherboard = "";
psu = "";
cost = 0.0;
serialnumber = 0;
depreciation = 0.0;
}
// Constructor with data members
Computer(String c, int r, String g, String m, String p, double co, int sn, double d) {
cpu = new String(c);
ram = r;
gpu = new String(g);
motherboard = new String(m);
psu = new String(p);
cost = co;
serialnumber = sn;
depreciation = d;
}
// Getters, allow retrieval of data members from outside of class
public String getCpu() {
return cpu;
}
public int getRam() {
return ram;
}
public String getGpu() {
return gpu;
}
public String getMotherboard() {
return motherboard;
}
public String getPsu() {
return psu;
}
public double getCost() {
return cost;
}
public int getSerialnumber() {
return serialnumber;
}
public double getDepreciation() {
return depreciation;
}
// Setters, allow setting of data members from outside of class
public void setCpu(String c) {
cpu = new String(c);
}
public void setRam(int r) {
ram = r;
}
public void setGpu(String g) {
gpu = new String(g);
}
public void setMotherboard(String m) {
motherboard = new String(m);
}
public void setPsu(String p) {
psu = new String(p);
}
public void setCost(double co) {
cost = co;
}
public void setSerialnumber(int sn) {
serialnumber = sn;
}
public void setDepreciation(double d) {
depreciation = d;
}
// Boolean below will compare computers to see if equal
// based on same motherboard SN#.
public boolean equals(Computer c) {
if (this.serialnumber == (c.serialnumber)) {
return true;
} else {
return false;
}
}
// To string method will print characteristics about object.
public String toString() {
return ("CPU:\t\t" + cpu + "\n" + "RAM:\t\t" + ram + "\n" + "GPU:\t\t" + gpu + "\n" + "Motherboard:\t"
+ motherboard + "\n" + "PSU:\t\t" + psu + "\n" + "Cost:\t\t" + "$" + cost + "\n" + "SN#:\t\t"
+ serialnumber + "\n" + "Depreciation:\t" + "$" + depreciation + " (annually)");
}
// A method to depreciate the cost of the computer
// The formula is observed below, but this is a
// straight line depreciation equation, calculated based
// on the values the user passes into the function. This method
// will show an output of annual depreciation based on useful
// life, entered in "years" by the user.
public void depreciate(double purchasePrice, double salvageValue, double lifeSpanYears) {
double depreciableCost;
double annualDepreciation;
depreciableCost = purchasePrice - salvageValue;
annualDepreciation = depreciableCost / lifeSpanYears;
depreciation = annualDepreciation;
}
// A method to upgrade the ram or the video card
// The method will accpet argumetns for ram (in int) and a gpu (string).
public void upgrade(int newRam, String newGpu) {
ram = newRam;
gpu = new String(newGpu);
}
}
For me your implementation is perfectly working. Here I attached screenshot, may be you did something wrong during console input when you are taking input search text.Console out put snapshot
Ok,
Mr. Mopp was right - but I had to remove the Computer class before the searchResult variable for it to work, and make a var type of String, SearchResult. So this works for me below:
String searchResult;
while (!"q".equals(search)) {
searchResult = cpu1.findSys(search);
if (searchResult != null) {
System.out.println(searchResult);
}
else {
System.out.println("not found");
}
You don't want to compare if search == null because search is the user input. You want to check if the result of the search is null:
while (!"q".equals(search)) {
Computer searchResult = cpu1.findSys(search);
if (searchResult != null) {
System.out.println(searchResult);
}
else {
System.out.println("not found");
}
You also should change the return type of findSys to be Computer. Returning just a String limits the usefulness of the function:
public Computer findSys(String c) {
for (int i = 0; i < sysNumbers; i++) {
if (systems[i] != null && systems[i].getCpu().equals(c))
return systems[i];
}
return null;
}

If thread-safe, any chances to make more concurrent

The class AnagramGameDefault simulates an anagram game.
The submitScore() should recalculate the positions, the one with highest score has position 1, there can be several players on the same position.
The getLeaderBoard() fetches the entry for a user plus two above and two below.
The concerns I have :
I tested the code for multiple threads and I guess it's working but I would like to know if there are some race conditions or failure in sharing state in the code
I have used quite a stringent mutually exclusive locking by using 'synchronized'. I don't think this can be avoided as submitScore() and getLeaderBoard() rely heavily on sorting and correct positions of score but is there a possibility ? I read a bit about ReentrantLock but it's more suitable where there are multiple reads and lesser writes, in this case, even the reads need calculations.
public enum AnagramGameDefault{
INSTANCE;
private Map<String, Entry> leaderBoardUserEntryMap;
{
leaderBoardUserEntryMap = new LinkedHashMap<>();
}
public int calculateScore(String word, String anagram) {
if (word == null || anagram == null) {
throw new NullPointerException("Both, word and anagram, must be non-null");
}
char[] wordArray = word.trim().toLowerCase().toCharArray();
char[] anagramArray = anagram.trim().toLowerCase().toCharArray();
int[] alphabetCountArray = new int[26];
int reference = 'a';
for (int i = 0; i < wordArray.length; i++) {
if (!Character.isWhitespace(wordArray[i])) {
alphabetCountArray[wordArray[i] - reference]++;
}
}
for (int i = 0; i < anagramArray.length; i++) {
if (!Character.isWhitespace(anagramArray[i])) {
alphabetCountArray[anagramArray[i] - reference]--;
}
}
for (int i = 0; i < 26; i++)
if (alphabetCountArray[i] != 0)
return 0;
return word.length();
}
public void submitScore(String uid, int score) {
Entry newEntry = new Entry(uid, score);
sortLeaderBoard(newEntry);
}
private void sortLeaderBoard(Entry newEntry) {
synchronized (leaderBoardUserEntryMap) {
leaderBoardUserEntryMap.put(newEntry.getUid(), newEntry);
// System.out.println("Sorting for " + newEntry);
List<Map.Entry<String, Entry>> list = leaderBoardUserEntryMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Collections.reverseOrder())).collect(Collectors.toList());
leaderBoardUserEntryMap.clear();
int position = 0;
int previousPosition = 0;
int currentPosition = 0;
for (Map.Entry<String, Entry> entry : list) {
currentPosition = entry.getValue().getScore();
if (!(currentPosition == previousPosition))
position++;
entry.getValue().setPosition(position);
leaderBoardUserEntryMap.put(entry.getKey(), entry.getValue());
previousPosition = currentPosition;
}
}
}
public List<Entry> getLeaderBoard(String uid) {
final int maxEntriesAroundAnEntry = 2;
if (!leaderBoardUserEntryMap.containsKey(uid))
return Collections.emptyList();
Entry userEntry = null;
final List<Entry> leaderBoard = new ArrayList<>();
List<Entry> lowerEntries = null;
List<Entry> higherEntries = null;
synchronized (leaderBoardUserEntryMap) {
printBoard();
userEntry = leaderBoardUserEntryMap.get(uid);
int userPosition = userEntry.getPosition();
int upperPosition = userPosition - maxEntriesAroundAnEntry;
int lowerPosition = userPosition + maxEntriesAroundAnEntry;
// Higher entries
higherEntries = leaderBoardUserEntryMap.values().stream()
.filter(entry -> (entry.getPosition() < userPosition && entry.getPosition() >= upperPosition))
.map(entry -> new Entry(entry.getUid(), entry.getScore(), entry.getPosition()))
.collect(Collectors.toList());
// Lower entries
lowerEntries = leaderBoardUserEntryMap.values().stream()
.filter(entry -> (entry.getPosition() > userPosition && entry.getPosition() <= lowerPosition))
.map(entry -> new Entry(entry.getUid(), entry.getScore(), entry.getPosition()))
.collect(Collectors.toList());
userEntry = new Entry(userEntry.getUid(), userEntry.getScore(), userEntry.getPosition());
// }
if (higherEntries != null && !higherEntries.isEmpty()) {
if (higherEntries.size() >= maxEntriesAroundAnEntry) {
higherEntries = higherEntries.subList(higherEntries.size() - maxEntriesAroundAnEntry,
higherEntries.size());
}
leaderBoard.addAll(higherEntries);
}
leaderBoard.add(userEntry);
if (lowerEntries != null && !lowerEntries.isEmpty()) {
if (lowerEntries.size() >= maxEntriesAroundAnEntry) {
lowerEntries = lowerEntries.subList(0, maxEntriesAroundAnEntry);
}
leaderBoard.addAll(lowerEntries);
}
}
return leaderBoard;
}
public void printBoard() {
System.out.println("---------Start : Current leader board---------");
leaderBoardUserEntryMap.forEach((key, value) -> {
System.out.println("BOARD ENTRY : " + key + " : " + value);
});
System.out.println("---------End : Current leader board---------");
}
}
The Entry POJO :
public class Entry implements Comparable<Entry> {
private String uid;
private int score;
private int position;
public Entry(String uid, int score) {
this.uid = uid;
this.score = score;
}
public Entry(String uid, int score, int position) {
this.uid = uid;
this.score = score;
this.position = position;
}
public Entry() {
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + score;
result = prime * result + ((uid == null) ? 0 : uid.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Entry other = (Entry) obj;
if (score != other.score)
return false;
if (uid == null) {
if (other.uid != null)
return false;
} else if (!uid.equals(other.uid))
return false;
return true;
}
#Override
public String toString() {
return "Entry [uid=" + uid + ", score=" + score + ", position=" + position + "]";
}
#Override
public int compareTo(Entry o) {
// TODO Auto-generated method stub
if (o == null)
return -1;
return Integer.compare(score, o.getScore());
}
}
The tester class :
public class AnagramGameDefaultDemo {
public static void main(String[] args) {
if (args == null || args.length < 1) {
System.out.println("Enter testing approach - 1 for single threaded, 2 for multi-threaded");
return;
}
switch (args[0]) {
case "1": {
new AnagramGameDefaultDemo().testSingleThreaded();
break;
}
case "2": {
new AnagramGameDefaultDemo().testMultithreaded();
break;
}
default: {
System.out.println("Enter proper option(1 or 2)");
break;
}
}
}
private void testMultithreaded() {
Map<String, String> stringAnagramMap = new HashMap<>();
CountDownLatch countDownLatchOne = new CountDownLatch(1);
stringAnagramMap.put("raw", "war");
stringAnagramMap.put("raw", "wars");
AnagramGamePlayer jake = new AnagramGamePlayer("jake", stringAnagramMap, countDownLatchOne);
new Thread(jake, "jake").start();
stringAnagramMap.clear();
stringAnagramMap.put("tool", "loot");
AnagramGamePlayer ace = new AnagramGamePlayer("ace", stringAnagramMap, countDownLatchOne);
new Thread(ace, "ace").start();
stringAnagramMap.clear();
stringAnagramMap.put("William Shakespeare", "I am a weakish speller");
AnagramGamePlayer max = new AnagramGamePlayer("max", stringAnagramMap, countDownLatchOne);
new Thread(max, "max").start();
stringAnagramMap.clear();
stringAnagramMap.put("School master", "The classroom");
AnagramGamePlayer tBone = new AnagramGamePlayer("tBone", stringAnagramMap, countDownLatchOne);
new Thread(tBone, "tBone").start();
stringAnagramMap.clear();
countDownLatchOne.countDown();
CountDownLatch countDownLatchTwo = new CountDownLatch(1);
stringAnagramMap.put("Punishments", "Nine Thumps");
AnagramGamePlayer razor = new AnagramGamePlayer("razor", stringAnagramMap, countDownLatchTwo);
new Thread(razor, "razor").start();
stringAnagramMap.clear();
stringAnagramMap.put("Dormitory", "Dirty Room");
AnagramGamePlayer chip = new AnagramGamePlayer("chip", stringAnagramMap, countDownLatchTwo);
new Thread(chip, "chip").start();
stringAnagramMap.clear();
countDownLatchTwo.countDown();
CountDownLatch countDownLatchThree = new CountDownLatch(1);
stringAnagramMap.put("Mother in law", "Hitler woman");
AnagramGamePlayer dale = new AnagramGamePlayer("dale", stringAnagramMap, countDownLatchThree);
new Thread(dale, "dale").start();
countDownLatchThree.countDown();
stringAnagramMap.clear();
}
private final class AnagramGamePlayer implements Runnable {
private Map<String, String> stringAnagramMap = new HashMap<>();
private String uid;
private CountDownLatch countDownLatch;
public AnagramGamePlayer(String uid, Map<String, String> stringAnagramMap, CountDownLatch countDownLatch) {
this.stringAnagramMap.putAll(stringAnagramMap);
this.uid = uid;
this.countDownLatch = countDownLatch;
}
#Override
public void run() {
AnagramGameDefault anagramGameDefault = AnagramGameDefault.INSTANCE;
try {
countDownLatch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Player " + uid + " started playing with " + stringAnagramMap);
stringAnagramMap.entrySet().forEach(entry -> {
anagramGameDefault.submitScore(uid,
anagramGameDefault.calculateScore(entry.getKey(), entry.getValue()));
printLeaderBoard(uid, anagramGameDefault.getLeaderBoard(uid));
});
System.out.println("Player " + uid + " completed playing");
}
}
private void testSingleThreaded() {
AnagramGameDefault anagramGameDefault = AnagramGameDefault.INSTANCE;
anagramGameDefault.submitScore("Jake", 3);
anagramGameDefault.submitScore("Ace", 7);
anagramGameDefault.submitScore("Max", 1);
anagramGameDefault.submitScore("T-Bone", 14);
anagramGameDefault.submitScore("Razor", 6);
anagramGameDefault.submitScore("Razor", 7);
anagramGameDefault.submitScore("He-Man", 4);
anagramGameDefault.submitScore("Men-at-Arms", 8);
anagramGameDefault.submitScore("BattleCat", 3);
anagramGameDefault.submitScore("Jake", 2);
anagramGameDefault.submitScore("BattleCat", 3);
anagramGameDefault.printBoard();
anagramGameDefault.submitScore("Men-at-Arms", 21);
anagramGameDefault.submitScore("Orko", 20);
anagramGameDefault.submitScore("Jake", 4);
anagramGameDefault.printBoard();
System.out.println();
printLeaderBoard("user5", anagramGameDefault.getLeaderBoard("user5"));
System.out.println();
printLeaderBoard("user4", anagramGameDefault.getLeaderBoard("user4"));
System.out.println();
printLeaderBoard("user15", anagramGameDefault.getLeaderBoard("user15"));
System.out.println();
List<Entry> entries = anagramGameDefault.getLeaderBoard("user1");
printLeaderBoard("user1", entries);
System.out.println("Changing state of the received entries");
entries.forEach(entry -> {
entry.setPosition(1);
entry.setScore(0);
});
anagramGameDefault.printBoard();
printLeaderBoard("user1", anagramGameDefault.getLeaderBoard("user1"));
}
private static void printLeaderBoard(String user, List<Entry> leaderBoard) {
if (user == null || leaderBoard.isEmpty()) {
System.out.println("Either user " + user + " doesn't exist or leader board is empty " + leaderBoard);
}
System.out.println("**********Printing leader board for " + user);
leaderBoard.forEach(System.out::println);
System.out.println("**********");
}
}
It seems the only shared state you have in the whole thing is leaderBoardUserEntryMap. You synchronize on it when updating in sortLeaderBoard. In getLeaderBoard for some reason you don't yet synchronize on it when checking if (!leaderBoardUserEntryMap.containsKey(uid)). That's minor, but you should do it as well. Later, you synchronize on it when constructing the leader board.
From this point of view your synchronization seems adequate.
What I find a bit problematic is that your Entry is mutable and stores position. This makes your update operation more problematic. You have to re-sort and re-set positions on every update. And you're locking all other update or even read operations. I'd avoid mutable objects in multithreaded code.
I'd use a SortedSet instead and let the implementation handle sorting. To find out the position of the element you'd just do set.headSet(element).size() + 1. So no need to store position at all.
I initially wanted to suggest using concurrent collection implementations like ConcurrentHashSet which would allow "full concurrency of retrievals and adjustable expected concurrency for updates". Basically, retrievals could be non-blocking, only updates were.
However, this won't help much as your "retrieval" logic (creating the leader board around target entry) is not so simple and involves several reads. So I think it's better not use concurrent collections but instead actually synchronize on the collection and make updates and retrievals as compact as possible. If you give up on the idea of having position in Entry then update is a trivial add. Then you'll only need to read entries around the entry as fast as possible (within the synchronized block). This should be quite fast with tree sets.

How do I change a Variable, in a Java Class

I have to be able to convert some variables in my class. I have a boolean variable, WaGa (Stands for Workstation/Gaming computer), and if it's true, I want to convert String WorGam
I have to do this through service and support methods, and I keep trying, but I constenly fail. It just prints out what's in the driver. HELP.
public class Graphics
//instance data
{
private int Ram;
private String Brand;
private int Res;
private int BiWi;
private int BaCl;
private boolean K4;
private boolean WaGa;
private String WorGam;
//boolean WaGa, boolean K4, int BaCl, int BiWi, int Res, String Brand, int Ram
public Graphics (int R, String B, int Re, int Bi, int Ba, boolean K4, boolean Wa, String Wor ) // constructor
{
Ram = R;
Brand = B;
Res = Re;
BiWi = Bi;
BaCl = Ba;
K4 = K4;
WaGa = Wa;
Wor = WorGam;
}
public int get_Ram() //Accessor Method - there are 3 of them
{
return Ram;
}
public String get_Brand() //Accessor Method - there are 3 of them
{
return Brand;
}
public int get_Res() //Accessor Method - there are 3 of them
{
return Res;
}
public int get_BiWi() //Accessor Method - there are 3 of them
{
return BiWi;
}
public int get_BaCl()
{
return BaCl;
}
public boolean get_K4()
{
return K4;
}
public String WorGam(boolean WaGa)
{
String WorGam;
if ( WaGa == true) {
return WorGam = "Workstation";
} else {
return WorGam = "True";
}
}
public String toString()
{
return ("Ram" + " " + Ram + ". " + "Brand:" + " " + Brand + ". " + "Resolution" + " " + Res + ". " + "Processer" + " " + BiWi + "." + " " + "Base Clock" + " " + BaCl+ " " + "K4?" + " " + K4+ " " +WorGam);
}
}
public class Graphicse_Driver
{
public static void main(String [] args)
{
Graphics unique=new Graphics(4, "Nvinda", 6, 7, 9, false, false, "sdf" );
System.out.println(unique);
You may need to reread you code to make sure there aren't any other mistakes in your code, but this is the root of your problem.
In order to access the WarGam getter, you need to call:
System.out.println(unique.WarGam());
When you do System.out.println(unique), you are trying to print out the entire Graphics object instead of just the WarGam string.
You then should change your WarGam() method to look like the following:
public String WorGam()
{
if (WaGa) {
return "Workstation";
}
return "Gaming";
}
Here is a more in depth explanation of the changes:
WaGa is a private variable of your Graphics class. Since the WarGam() method is in the same Graphics class, it already had access to the WaGa variable, so you do not need to pass it in.
if(WaGa == true) is just a wordier way of writing if(WaGa).
Instead of creating a String WorGam variable, you can just return the string you want directly.
The else surrounding the second return is unnessary since that code will only be hit if the first return is skipped.
After these changes, the private String WarGam variable is really not necessary either.
public String worGam(boolean waGa) {
if (waGa)
return "Workstation";
else
return "Gaming";
}
You need to correct your worGam() function:
public String worGam(boolean waGa) {
if (waGa == true)
return "Workstation";
else
return "True";
}
And the main() function:
public static void main(String [] args) {
Graphics unique = new Graphics(4, "Nnn", 6, 7, 9, false, false, "xxx");
System.out.println(unique.WorGam(false));
}

Find Highest Value in Enum

I'm making a class for a game I'm making and cannot figure out how to return the highest 'reward' to a user:
What I'm trying to get it to do is return the reward based on the player's kills. If he has 38 kills, it should return only 300. Not 150 aswell.
class EnumTest {
private static int playerKills = 38;
private enum KillReward {
BEGINNER(10, 150),
JUNIOR(25, 300),
EXPERT(50, 500),
CHAMPION(100, 1000);
private KillReward(int kills, int reward) {
this.kills = kills;
this.reward = reward;
}
private int kills, reward;
private int getKillsNeeded() {
return kills;
}
private int getKillReward() {
return reward;
}
}
public static void main (String[] args) {
for (KillReward k : KillReward.values()) {
if (playerKills > k.getKillsNeeded()) {
System.out.println("Kills: " + playerKills + "; Reward: " + k.getKillReward());
}
}
}
}
There are a couple ways of doing this.
One easy way is assign the reward to a variable. At the end of that loop the reward variable below will be the highest kill reward that was applicable
public static void main (String[] args) {
KillReward reward = null;
for (KillReward k : KillReward.values()) {
if (playerKills > k.getKillsNeeded()) {
reward = k;
}
}
System.out.println("Kills: " + playerKills + "; Reward: " + k.getKillReward());
}
Note that this relies on the enum being listed in order which can sometimes be fragile. For example, if a new KillReward enum is added after CHAMPION but has a lower killsNeeded value, then it will not return the proper value.
A better solution would be to create a Comparator and use it to sort the enum values by killsNeeded first, thus ensuring that they are always in order. If you also sort it in descending order, then you can also break from your loop once you hit the first applicable one.
class EnumTest {
private enum KillReward {
BEGINNER(10, 150), JUNIOR(25, 300), EXPERT(50, 500), CHAMPION(100, 1000);
// Sort KillRewards once at initialization
private static final List<KillReward> sortedKillRewards = new ArrayList<KillReward>();
static {
for (KillReward k : values())
sortedKillRewards.add(k);
Collections.sort(sortedKillRewards, new Comparator<KillReward>() {
#Override
public int compare(KillReward o1, KillReward o2) {
return (o1.kills - o2.kills) * -1; // multiplying by -1 makes it
// descending
}
});
}
private KillReward(int kills, int reward) {
this.kills = kills;
this.reward = reward;
}
private int kills, reward;
private int getKillsNeeded() {
return kills;
}
private int getKillReward() {
return reward;
}
public static KillReward forKills(int killCount) {
for (KillReward k : sortedKillRewards)
if (killCount >= k.kills)
return k;
// must not have enough kills for any reward
return null;
}
}
public static void main(String[] args) {
int kills = 9;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
kills = 10;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
kills = 38;
System.out.println("Kills: " + kills + "; Reward: "
+ KillReward.forKills(kills));
}
}

Categories