Related
I'm attempting to Stream a list of Objects to aggregate the int min and int max based on the grouping of several Field attributes.
public class OccupancyEntry {
private final int entryID;
private final String attribute1;
private final String attribute2;
private final String attribute3;
private final int min;
private final int max;
public OccupancyEntry(int entryID, String attribute1, String attribute2, String attribute3, int min, int max) {
this.entryID = entryID;
this.attribute1 = attribute1;
this.attribute2 = attribute2;
this.attribute3 = attribute3;
this.min = min;
this.max = max;
}
}
I'd like to map the above Objects to a a list of the following:
public class OccupancyAggregated {
private final LocalDate date;
private final String attribute1;
private final String attribute2;
private final String attribute3;
private final int min;
private final int max;
public OccupancyAggregated(LocalDate date, String attribute1, String attribute2, String attribute3, int min, int max) {
this.date = date;
this.attribute1 = attribute1;
this.attribute2 = attribute2;
this.attribute3 = attribute3;
this.min = min; //these are summed values
this.max = max; //these are summed values
}
}
My attempt so far. Through the help of this answer I've been able to group the entries by a set of fields, but using this method I can no longer get the mapping and reducing functions to work.
OccupancyEntry entry1 = new OccupancyEntry(1, "1", "2", "3", 1, 10);
OccupancyEntry entry2 = new OccupancyEntry(1, "1", "2", "3", 1, 10);
OccupancyEntry entry3 = new OccupancyEntry(1, "A", "B", "C", 1, 10);
ArrayList<OccupancyEntry> occupancyEntries = new ArrayList<>();
occupancyEntries.add(entry1);
occupancyEntries.add(entry2);
occupancyEntries.add(entry3);
LocalDate date = LocalDate.now();
ArrayList<OccupancyAggregated> aggregated = new ArrayList<>();
Map<List<String>, List<OccupancyEntry>> collect = occupancyEntries.stream()
.collect(groupingBy(x -> Arrays.asList(x.getAttribute1(), x.getAttribute2(), x.getAttribute3())));
The sought after output. Where the min and max fields are reduced from from the entirety of the grouped OccupancyEntry list.
ArrayList<OccupancyAggregated> aggregated = new ArrayList<>();
My previous attempts have consisted out of creating multiple streams which reduce either the min field or the max field based on String concatenated attribute fields.
final Map<String, Integer> minOccupancy = occupancyEntries.stream()
.collect((groupingBy(OccupancyEntry::getGroupedAttributes,
mapping(OccupancyEntry::getMin,
reducing(0, Integer::sum)))));
final Map<String, Integer> maxOccupancy = occupancyEntries.stream()
.collect((groupingBy(OccupancyEntry::getGroupedAttributes,
mapping(OccupancyEntry::getMax,
reducing(0, Integer::sum)))));
Afterwards I'd for loop through one of the maps and then start creating a new list with OccupancyAggregated objects and look up the values in both maps to construct it. This seemed much too convoluted.
I've also been trying to perform the reduce operation in one pass using this answer but I'm not sure how to get it to map to the new Class type properly.
occupancyEntries.stream()
.reduce((x, y) -> new OccupancyAggregated(
date,
x.getAttribute1(),
x.getAttribute2(),
x.getAttribute3(),
x.getMin() + y.getMin(),
x.getMax() + y.getMax()
))
.orElse(new OccupancyAggregated(date, "no data", "no data", "no data", 0, 0));
This is a very common request that's messy to solve with streams, because there's no concept of an "empty" or "identity" instance of the aggregate. I prefer the semi-imperative approach using a loop and Map.merge():
Map<List<String>, OccupancyAggregated> grouped = new HashMap<>();
for (OccupancyEntry e : occupancyEntries) {
grouped.merge(Arrays.asList(e.attribute1, e.attribute2, e.attribute3),
OccupancyAggregated.from(e, date),
OccupancyAggregated::merge);
}
List<OccupancyAggregated> aggregated = new ArrayList<>(grouped.values());
Where the merge() method looks like this:
public OccupancyAggregated merge(OccupancyAggregated o) {
return new OccupancyAggregated(
date, attribute1, attribute2, attribute3, min + o.min, max + o.max);
}
Alternatively, if you can make the aggregate mutable, you can update min/max in place:
OccupancyAggregated agg = grouped.computeIfAbsent(
Arrays.asList(e.attribute1, e.attribute2, e.attribute3),
attributes -> OccupancyAggregated.from(attributes, date));
agg.add(e.min, e.max);
I left out some of the new method implementations for the sake of brevity. Let me know if they need more elaboration.
You could do this:
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class AggregateToGetMinMax {
public static void main(String[] args) {
List<OccupancyEntry> list = setup();
List<OccupancyAggregated> result = aggregate(list, LocalDate.now());
System.out.println("\nresult: " + result);
}
public static List<OccupancyEntry> setup() {
OccupancyEntry[] entries = {
new OccupancyEntry(1, "1", "2", "3", 1, 10),
new OccupancyEntry(1, "1", "2", "3", 1, 10),
new OccupancyEntry(1, "A", "B", "C", 1, 10)
};
return List.of(entries);
}
public static List<OccupancyAggregated> aggregate(List<OccupancyEntry> occupancyEntries, LocalDate date) {
class MinMax {
int max = 0;
int min = 0;
public MinMax() {
}
public MinMax(OccupancyEntry oe) {
this.max = oe.max;
this.min = oe.min;
}
public MinMax add(MinMax mm) {
this.max += mm.max;
this.min += mm.min;
return this;
}
public String toString() {
return "min: " + min + "; max: " + max;
}
}
Map<String, List<OccupancyEntry>> groupedEntries = occupancyEntries.stream()
.collect(Collectors.groupingBy(OccupancyEntry::getGroupedAttributes));
System.out.println("grouped entries: ");
groupedEntries.entrySet().stream()
.forEach(entry -> System.out.println(entry));
List<OccupancyAggregated> result = groupedEntries.values().stream()
.map(list -> {
MinMax mm = list.stream()
.map(MinMax::new)
.reduce(
new MinMax(),
(MinMax acc, MinMax item) -> acc.add(item));
// It's okay to get(0) because all entries with have values with at least one item
// in the list.
OccupancyAggregated aggregated = new OccupancyAggregated(date, list.get(0), mm.min, mm.max);
return List.of(aggregated);
})
.collect(Collectors.toList()).stream()
.flatMap(List::stream)
.collect(Collectors.toList());
return result;
}
public static class OccupancyEntry {
private final int entryID;
private final String attribute1, attribute2, attribute3;
public final int min, max;
public OccupancyEntry(int entryID, String attribute1, String attribute2, String attribute3, int min, int max) {
this.entryID = entryID;
this.attribute1 = attribute1;
this.attribute2 = attribute2;
this.attribute3 = attribute3;
this.min = min;
this.max = max;
}
public String getGroupedAttributes() {
return List.of(attribute1, attribute2, attribute3).stream()
.collect(Collectors.joining("|"));
}
#Override
public String toString() {
return "OccupancyEntry [" + entryID + ", " + attribute1 + ", " + attribute2
+ ", " + attribute3 + ", " + min + ", " + max + "]";
}
}
public static class OccupancyAggregated {
final LocalDate date;
String attribute1, attribute2, attribute3;
private int min, max;
public OccupancyAggregated(LocalDate date, OccupancyEntry oe, int min, int max) {
this.date = date;
this.attribute1 = oe.attribute1;
this.attribute2 = oe.attribute2;
this.attribute3 = oe.attribute3;
this.min = min; // these are summed values
this.max = max; // these are summed values
}
#Override
public String toString() {
return "OccupancyAggregated: ( " +
attribute1 + ", " +
attribute2 + ", " +
attribute3 + ", " +
min + ", " +
max +
" )";
}
}
}
Note that I have separated the grouping and reducing so that one can more clearly focus on the reducing part of the work. Otherwise, building the entire operation straight through might become a bit tedious to mentally parse the important operation.
Output:
grouped entries:
A|B|C=[OccupancyEntry [1, A, B, C, 1, 10]]
1|2|3=[OccupancyEntry [1, 1, 2, 3, 1, 10], OccupancyEntry [1, 1, 2, 3, 1, 10]]
result: [OccupancyAggregated: ( A, B, C, 1, 10 ), OccupancyAggregated: ( 1, 2, 3, 2, 20 )]
The more succinct version of accumulate() looks like this:
public static List<OccupancyAggregated> aggregate(List<OccupancyEntry> occupancyEntries, LocalDate date) {
class MinMax {
int max = 0;
int min = 0;
public MinMax() {
}
public MinMax(OccupancyEntry oe) {
this.max = oe.max;
this.min = oe.min;
}
public MinMax add(MinMax mm) {
this.max += mm.max;
this.min += mm.min;
return this;
}
public String toString() {
return "min: " + min + "; max: " + max;
}
}
List<OccupancyAggregated> result = occupancyEntries.stream()
.collect(Collectors.groupingBy(OccupancyEntry::getGroupedAttributes))
.values().stream()
.map(list -> {
MinMax mm = list.stream()
.map(MinMax::new)
.reduce(
new MinMax(),
(MinMax acc, MinMax item) -> acc.add(item));
// It's okay to get(0) because all entries with have values with at least one item
// in the list.
OccupancyAggregated aggregated = new OccupancyAggregated(date, list.get(0), mm.min, mm.max);
return List.of(aggregated);
})
.collect(Collectors.toList()).stream()
.flatMap(List::stream)
.collect(Collectors.toList());
return result;
}
I have a question which requires me to create an enum class with two fields, name (String), and then create a hashmap to generate the output with foreach loops method here.
Expected output:
1 = APPLE, price = 20
2 = STRAWBERRY, price = 70
I try to create two hashmaps to get the output and for loops to get the value but the output is not what I want. May I know how to print the price with the relevant fruits only?
My output:
1 = APPLE
2 = STRAWBERRY
,price=20
,price=70
Enum code here:
enum Fruit {
APPLE("APPLE", 20), STRAWBERRY("STRAWBERRY", 70);
private final int price;
private final String name;
private Fruit(String name, int price) {
this.name = name;
this.price = price;
}
}
main class here
public static void main(String[] args) {
HashMap<Integer, Fruit> foodTable = new HashMap<>();
HashMap<Fruit, Integer> priceTable = new HashMap<>();
foodTable.put(1, Fruit.APPLE);
priceTable.put(Fruit.APPLE, Fruit.APPLE.price);
foodTable.put(2, Fruit.STRAWBERRY);
priceTable.put(Fruit.STRAWBERRY, Fruit.STRAWBERRY.price);
for (Map.Entry<Integer, Fruit> set : foodTable.entrySet()) {
System.out.println(set.getKey() + " = " + set.getValue());
}
for (Map.Entry<Fruit, Integer> set1 : priceTable.entrySet()) {
System.out.println("," +
"price" + "=" + set1.getValue());
}
}
You don't need maps at all, as an enum provides a method to return its values.
Given the enum
enum Fruit {
APPLE("APPLE", 20), STRAWBERRY("STRAWBERRY", 70);
private final int price;
private final String name;
private Fruit(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public int getPrice() { return price; }
}
You can simply write:
int pos = 0;
for (Fruit fruit : Fruit.values()) {
pos++;
System.out.printf("%d = %s, price = %d%n",
pos, fruit.getName(), fruit.getPrice());
}
Edit: as an enum value also knows its ordinal position you can simplify this to
for (Fruit fruit : Fruit.values()) {
System.out.printf("%d = %s, price = %d%n",
fruit.ordinal()+1, fruit.getName(), fruit.getPrice());
}
I came across a problem where one needs to check for rows in Array1 that are not in Array2 and append it at the end of Array2 in Java. The rows that are common with regard to the first column i.e. name can be skipped. In the below example, the rows in firstarray with "Nick" and "Bruce" should be appended at the end of secondarray.
I have edited the arrays again slightly to get more clarity.
String firstarray[][] = {
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","New York"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
The solution should be like:
secondarray[][]:
{"Adam","01-Dec-1980","Commerce","New York"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}}
Collect the names of the second array to a set, iterate over your first array and filter out those elements which are in the set and collect the result in a third array (or any other collection). Append this collection to your second array.
public static void main(String[] args){
String firstarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Sujay Muramalla","08-Jan-1985","Arts","London"}};
//collect names of second array to set
Set<String> secondSet = Arrays.stream(secondarray).map(e -> e[0]).collect(Collectors.toSet());
//stream over your first array and keep only those which are not in the above set
String[][] third = Arrays.stream(firstarray)
.filter(e -> !secondSet.contains(e[0]))
.toArray(String[][]::new);
//stream over second and delta (third array) and collect to a result array
String[][] result = Stream.concat(Arrays.stream(secondarray), Arrays.stream(third))
.toArray(String[][]::new);
//output
Arrays.stream(result).forEach(e ->{
System.out.println(Arrays.toString(e));
});
}
You should not be using arrays for this. Much better to use libraries already doing the job.
Even if the data you receive is already that way, you can converted first to a Map<String, Person> it will be more efficient, and readable code.
With arrays or many solutions not using some hashing system, you end up with exponential complexity O(n2), so not efficient.
Convert at least secondarray
Map<String, Person> secondMap = new HashMap();
for(String [] row : secondarray){
secondMap.put(row[0], new Person(row[0], row[1], row[2], row[3]));
}
Then put in the map if not already there
for(String[] row : firstarray){
if(!secondMap.containsKey(row[0])){
secondMap.put(row[0], new Person(row[0], row[1], row[2], row[3]));
}
}
Where Person class could be simply defined as
private static class Person{
public Person(String name, String birth, String area, String city){
this.name = name;
this.birth = birth;
this.area = area;
this.city = city;
}
String name;
String birth;
String area;
String city;
}
if you want check more than the first element of the array you can use nopens solution and replace e[0] with Arrays.toString(e).
A cleaner way if this is possible for you, is to use a list with a object and use a id for checking equals or override the hashcode function of the customer object.
You can also check for name and birth like that:
class Customer {
private String name;
private String birth;
private String type;
private String location;
public Customer(String name, String birth, String type, String location) {
this.name = name;
this.birth = birth;
this.type = type;
this.location = location;
}
#Override
public String toString() {
return "Customer [name=" + name + ", birth=" + birth + ", type=" + type + ", location=" + location + "]";
}
}
List<Customer> firstList = new ArrayList<Customer>();
firstList.add(new Customer("Adam", "01-Dec-1980", "Commerce", "Kansas"));
firstList.add(new Customer("John", "04-Feb-1982", "Economics", "Leeds"));
firstList.add(new Customer("Mathias", "08-Jan-1985", "Arts", "London"));
firstList.add(new Customer("Nick", "09-06-1974", "History", "Johanesburg"));
firstList.add(new Customer("Bruce", "13-08-1975", "Philosophy", "Seattle"));
List<Customer> secondList = new ArrayList<Customer>();
secondList.add(new Customer("Adam", "01-Dec-1980", "Commerce", "Kansas"));
secondList.add(new Customer("John", "04-Feb-1982", "Economics", "Leeds"));
secondList.add(new Customer("Mathias", "08-Jan-1985", "Arts", "London"));
for (Customer customer : firstList) {
if (containsNameAndBirth(secondList, customer) == false) {
secondList.add(customer);
}
}
for (Customer customer : secondList) {
System.out.println(customer);
}
}
public static boolean containsNameAndBirth(final List<Customer> list, final Customer customer) {
return list.stream().filter(o -> o.name.equals(customer.name) && o.birth.equals(customer.birth)).findFirst()
.isPresent();
}
EDIT 1 - Using Custom Class
I suggest you to always use List over Array.
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
static class Person {
public String name;
public String birthDate;
public String field;
public String city;
public static Person fromArray(String[] data) {
Person p = new Person();
if (data.length == 4) {
p.name = data[0];
p.birthDate = data[1];
p.field = data[2];
p.city = data[3];
} else {
// Handle me
}
return p;
}
#Override
public String toString() {
return new StringBuilder("[").append(name)
.append(",").append(birthDate)
.append("] learns ").append(field)
.append(" at ").append(city).toString();
}
}
public static void main(String[] args) {
String firstArray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondArray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
List<Person> finalList = getFinalList(firstArray, secondArray);
// Display
System.out.println(finalList);
}
public static List<Person> getFinalList(String[][] arr1, String[][] arr2) {
// First cast to Lists of persons
List<Person> firstList = Arrays.asList(arr1).stream().map(Person::fromArray).collect(Collectors.toList());
List<Person> secondList = Arrays.asList(arr2).stream().map(Person::fromArray).collect(Collectors.toList());
// Get names in secondList
Set<String> existingNames = secondList.stream().map(p -> p.name).collect(Collectors.toSet());
System.out.println("Names: "+ existingNames);
firstList.forEach(person -> {
if (! existingNames.contains(person.name)) {
secondList.add(person);
}
});
return secondList;
}
}
I upvoted nopens solutions cause it is nice one
Here another that uses maps and makes use of a logic of skipping common keys using removeAll on the keySet of the map, which was a functional method existing befor Java turned "more" functional
static public <T> Map<T,T[]> arrayToMap(T[][] array, int i) {
return Arrays.stream(array).collect(Collectors.toMap(e -> e[i], e -> e));
}
public static void main(String[] args){
String firstarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Sujay Muramalla","08-Jan-1985","Arts","London"}};
Map<String,String[]> firstMap = arrayToMap(firstarray, 0);
Map<String,String[]> secondMap = arrayToMap(secondarray, 0);
secondMap.keySet().removeAll(firstMap.keySet());
firstMap.putAll(secondMap);
String[][] result = firstMap.values().stream().toArray(String[][]::new);
//output
Arrays.stream(result).forEach(e ->{
System.out.println(Arrays.toString(e));
});
}
sidenote: in arrayToMap you can choose which column you use as key.
And the logic could be even reduced to this 3 lines:
Map<String,String[]> firstMap = arrayToMap(firstarray, 0);
firstMap.putAll(arrayToMap(secondarray, 0));
String[][] result = firstMap.values().stream().toArray(String[][]::new);
since inserting a value with the same key overwrites the existing one and you get the same if the values are the same in case of equal keys.
A simple an efficient way to do it (if you don't care about ordering) is the following:
Time complexity: O(nlog(n))
Space complexity: O(n+m)
import java.util.Arrays;
public class Main {
public static void main(String ... args) {
String firstarray[][] = {
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"},
{"Nick","09-06-1974","History","Johanesburg"},
{"Bruce","13-08-1975","Philosophy","Seattle"}};
String secondarray[][] = {
{"Adam","01-Dec-1980","Commerce","Kansas"},
{"John","04-Feb-1982","Economics","Leeds"},
{"Mathias","08-Jan-1985","Arts","London"}};
String result [][] = new String[firstarray.length
+ secondarray.length][firstarray[0].length];
// sort firstarray
java.util.Arrays.sort(firstarray, new java.util.Comparator<String[]>() {
public int compare(String [] a, String[] b) {
return a[0].compareTo(b[0]);
}
});
//sort secondarray
java.util.Arrays.sort(secondarray, new java.util.Comparator<String[]>() {
public int compare(String [] a, String[] b) {
return a[0].compareTo(b[0]);
}
});
int i = 0, j=0, k=0, cmp ;
for ( ;i < secondarray.length && j< firstarray.length;) {
cmp = firstarray[i][0].compareTo(secondarray[j][0]);
if(cmp ==0) {
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
i++; j++;
}else if( cmp <0){
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
i++;
} else {
System.arraycopy(secondarray[j], 0, result[k++], 0, 4);
j++;
}
}
// copy the remaining if any from firstarray to the result
for (; i < firstarray.length; i++) {
System.arraycopy(firstarray[i], 0, result[k++], 0, 4);
}
// copy the remaining if any from secondarray to the result
for (; j < secondarray.length; j++) {
System.arraycopy(secondarray[j], 0, result[k++], 0, 4);
}
//resize it
secondarray = Arrays.copyOf(result, k);
// just print the secondarray
for (int x = 0; x < secondarray.length; x++) {
for (int y = 0; y < 4; y++) {
System.out.print(secondarray[x][y] + ",");
}
System.out.println("");
}
}
}
I have a ScenarioGenerator class to generate the Scenario. And in the Audit class, I set the function to decide how many scenarios to be generated and wanted to print out the occurrence of each enum type (Gender in this case).
I've referred to Counting an Occurrence in an Array (Java)
I used the frequency method to count the occurrence, but when there are many scenarios generated, I don't know how to aggregate the data. What I've tried was:
Generate the scenarios and add each scenario in the ArrayList
For each scenario, I get the passenger ArrayList, and save their attributes in different collections.
For each collection, print out its value and the count.
I can simply do like this:
int maleCount = Collections.frequency(genderCollection, Gender.MALE);
System.out.println(Gender.MALE.toString().toLowerCase() + ":" +maleCount);
However, if I have more enums and attributes, this method becomes lengthy and inefficient. Is there any way to deal with this?
Here is part of my Audit class:
public class Audit {
private Scenario scenario = new Scenario();
private ScenarioGenerator scenarioGenerator = new ScenarioGenerator();
private Person person = new Person();
private String auditType;
private int nubmerOfSimulation;
public enum Gender {
MALE, FEMALE, UNKNOWN;
}
public Audit() {
}
// create a specific number of random scenarios
// the concept similar to gamePlayed and gameWon in the last project
public void run(int runs) {
this.nubmerOfSimulation = runs;
ArrayList<Scenario> scenarios = new ArrayList<Scenario>();//create a arrayList to store each scenario
for (int i = 0; i < runs; i++) {
Scenario singleScenario = scenarioGenerator.generate();
scenarios.add(singleScenario); // save each scenario into a scenario arrayList
}
survialCalculator(scenarios);
}
public int survialCalculator(ArrayList<Scenario> scenarios) {
//person attribute arrayList
ArrayList<Gender> genderCollection = new ArrayList<Gender>();
for (Scenario scenario : scenarios) { // for each scenario, getting its passengers and pedestrians
ArrayList<Character> passengers = scenario.getPassengers();
for (Character passenger : passengers) {
if (passenger instanceof Person) {
Gender gender = ((Person) passenger).getGender();
//System.out.println(gender);
genderCollection.add(gender);
}
}
//try to print out the elements in the collection and its frequency, but failed
for(Gender genderElement : genderCollection) {
int genderCount = Collections.frequency(genderCollection, genderElement);
System.out.println(genderElement.toString().toLowerCase()+ ":" + genderCount);
}
return 1;
}
Here is part of my ScenarioGenerator:
public class ScenarioGenerator {
private Person person = new Person();
private Random random = new Random();
private int passengerCountMinimum;
private int passengerCountMaximum;
private int pedestrianCountMininum;
private int pedestrianCountMaximum;
private ArrayList<Character> passengers = new ArrayList<Character>();
private ArrayList<Character> pedestrians = new ArrayList<Character>();
public Person getRandomPerson() {
//need age, gender, bodyType, profession, pregnancy
int age = random.nextInt(100);
int profession = random.nextInt(person.getProfessionEnumLength());
int gender = random.nextInt(person.getGenderEnumLength());
int bodyType = random.nextInt(person.getBodyTypeEnumLength());
int pregnancy = random.nextInt(2);
Person people = new Person(age,
Profession.values()[profession],
Gender.values()[gender],
BodyType.values()[bodyType],
pregnancy == 1 ? true : false);
return people;
}
public Animal getRandomAnimal() {
//species, isPet
int age = random.nextInt(100);
int gender = random.nextInt(person.getGenderEnumLength());
int bodyType = random.nextInt(person.getBodyTypeEnumLength());
boolean isPet = random.nextBoolean();
String [] species = {"cat", "dog"};
int idx = random.nextInt(species.length);
String pickSpecies = species[idx];
Animal creature = new Animal(age, Gender.values()[gender], BodyType.values()[bodyType] , pickSpecies);
creature.setIsPet(isPet);
return creature;
}
//getters and setter of min and max numbers of passengers and pedestrians
public Scenario generate() {
//random number of passengers and pedestrians with random characteristics
//randomly red light or green light
//random condition "You" in the car
//abide by the minimum and maximum counts from above setters
//get random numbers abide by the setters
int numberOfPassengers = ThreadLocalRandom.current().nextInt(getPassengerCountMin(), getPassengerCountMax()+1);
int numberOfPedestrains =ThreadLocalRandom.current().nextInt(getPedestrianCountMin(), getPedestrianCountMax()+1);
boolean legalCrossing = random.nextBoolean();
//generate the number from the total numbers of passenger and pedestrians
int numberOfPersonPassenger = ThreadLocalRandom.current().nextInt(0, numberOfPassengers+1);
int numberOfAnimalPassenger = numberOfPassengers - numberOfPersonPassenger;
int numberOfPersonPedestrian = ThreadLocalRandom.current().nextInt(0, numberOfPedestrains+1);
int numberOfAnimalPedestrian = numberOfPedestrains - numberOfPersonPedestrian;
//generate the number of person passengers
for (int i = numberOfPersonPassenger; i > 0; i--) {
Person person = getRandomPerson();
passengers.add(person);
}
//remaining of the number of passengers should be animals
//no matter it is pet of not
for (int i = numberOfAnimalPassenger; i > 0; i--) {
Animal animal = getRandomAnimal();
passengers.add(animal);
}
for (int i = numberOfPersonPedestrian; i > 0; i--) {
Person person = getRandomPerson();
pedestrians.add(person);
}
for (int i =numberOfAnimalPedestrian; i > 0; i--) {
Animal animal = getRandomAnimal();
pedestrians.add(animal);
}
Scenario scenario = new Scenario(passengers, pedestrians, legalCrossing);
return scenario;
}
Here is part of my Scenario class:
public class Scenario {
private Random random;
private ArrayList<Character> passenagers = new ArrayList<Character>();
private ArrayList<Character> pedestrians = new ArrayList<Character>();
private boolean legalCrossing;
public Scenario() {
}
public Scenario(ArrayList<Character> passengers, ArrayList<Character> pedestrians, boolean isLegalCrossing) {
this.passenagers = passengers;
this.pedestrians = pedestrians;
this.legalCrossing = isLegalCrossing;
}
//getters and setters
The main class is to call the method:
public static void main(String[] args) throws Exception {
ethicalEngine.generate();
}
public void generate() {
Audit audit = new Audit();
audit.run(5);
}
You can use groupingBy stream operation on List<Gender> to get map of all gender type with count.
Map<Gender, Long> counted = genderCollection.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
And print this way
for (Map.Entry<Gender, Long> item : counted.entrySet()) {
System.out.println(item.getKey().toString()+ ":" + item.getValue());
}
You might consider using the Guava Multiset. As Stated here this data structure is more efficent when you have more attributes to count as the access to the number lies most of the time in O(1).
I am creating a program that handles a car dealership. The user has the opportunity to add a car in the store by creating a random 3 digit number.
Now the question is how I can search/delete cars depending on the 3 digit code?
I'm thinking that I need every code that the cars have to save it on an array so I can search and delete afterwards.
I have created a class and certain methods on it, I have also created 5 objects and I'm trying to see if it works on these 5.
Here is the method of the random number:
I use the metritis variable because I can't achieve to place correctly the values on the array so I have to give parameter of 1,2,3,4,5 so I can place them correctly to the array.
package antiprosopeia;
import java.util.Random;
public class Antiprosopeia {
private String company,colour;
private int model,horsePower,speed,price,specialCode,metritis;
private int[] codes = new int[]{0,0,0,0,0};
public Antiprosopeia(String company, String colour, int model, int horsePower, int speed, int price) {
this.company = company;
this.colour = colour;
this.model = model;
this.horsePower = horsePower;
this.speed = speed;
this.price = price;
}
public Antiprosopeia() {
company = ""; colour = ""; model = 0; horsePower = 0; speed = 0; price = 0;
}
public void setRandomNumber(int metritis) {
Random rand = new Random();
int randNum2 = rand.nextInt(900) + 100;
specialCode = randNum2;
codes[metritis] = specialCode;
}
public void printarray() {
for(int i=0; i<codes.length; i++) {
System.out.println(" " + codes[i]);}
}
public void Info() {
System.out.println("Company : " + company + "\nColour : " + colour + "\nModel : " + model + "\nHorse Power : " + horsePower +
"\nSpeed : " + speed + "\nPrice : " + price );
}
public static void main(String[] args) {
Antiprosopeia car1 = new Antiprosopeia("Toyota","red",333,100,2223,8000);
car1.setRandomNumber(0);
Antiprosopeia car2 = new Antiprosopeia("Mercedes","yellow",233,100,2990,9000);
car2.setRandomNumber(1);
Antiprosopeia car3 = new Antiprosopeia("Volkswagen","green",153,100,2780,6000);
car3.setRandomNumber(2);
Antiprosopeia car4 = new Antiprosopeia("Mitsubisi","white",678,140,2600,7000);
car4.setRandomNumber(3);
Antiprosopeia car5 = new Antiprosopeia("Porsche","black",390,1000,2000,30000);
car5.setRandomNumber(4);
}
}
[EDIT] Now when i call the printarray() method it seems that at my array only one value is hold and all the others are zer0s as i defined the array at start of my program
If I were doing this, I would use a HashMap. This way you know that you have a unique 3 digit number, and if you wanted to, you could also store more data. You could do something like:
HashMap<Integer, Car> cars = new HashMap<Integer, Car>();
This example would allow you to add a car object to the map. You don't have to that, but it's an option. If you didn't want to do that, you could do:
HashMap<Integer, String> cars = new HashMap<Integer, String>();
and then do:
cars.put(123, "Description of car");
Using a HashMap would give you more options when storing the data. This would also prevent you from creating an array with 1000 elements, all of which are 0 until you have a value for them. You could easily print out all your numbers by doing:
for(int number : cars.entrySet()){
System.out.println("My car number: " + number);
}
Searching for keys would extremely easy, as you could do:
String description = cars.getKey(123);
If description was null, you would know that there is no key for it.
Your issue is that each Antiprosopeia object has its own codes array. They are not shared.
If you really want each object to have a Random ID, then assign that within the constructor.
public class Antiprosopeia {
private String company,colour;
private int model,horsePower,speed,price,specialCode,metritis;
private int randID;
public Antiprosopeia(String company, String colour, int model, int horsePower, int speed, int price){
this.company = company;
this.colour = colour;
this.model = model;
this.horsePower = horsePower;
this.speed = speed;
this.price = price;
this.randID = new Random().nextInt(900) + 100;
}
public Antiprosopeia(){
this("", "", 0, 0, 0, 0);
}
public int getID() { return this.randID; }
#Override
public String toString() {
return String.format(
"Company : %s\n" +
"Colour : %s\n" +
"Model : %s\n" +
"Horse Power : %d\n" +
"Speed : %d\n" +
"Price : %d\n",
company, colour, model, horsePower, speed, price
);
}
If you want to print all those objects,
public static void main(String[] args) {
List<Antiprosopeia> cars = new ArrayList<Antiprosopeia>();
cars.add(new Antiprosopeia("Toyota","red",333,100,2223,8000));
cars.add(new Antiprosopeia("Mercedes","yellow",233,100,2990,9000));
for (int i = 0; i < cars.size(); i++) {
Antiprosopeia c = cars.get(i);
System.out.println(c.getID());
System.out.println(c);
}
}