I have 2 Lists:
// old list
List<Employee> oldList = new ArrayList<>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
Does anyone know how can I merge those 2 Lists to one List containing all the employees from both lists grouped by "PersonalNumber" and keeping the order of the elemets in newList?
newList comes from the Database with a predefined sorting, and I need to keep it that way, so I can't sort it again on the Java side
Result should be:
[
{"123", "new_name1", NEW},
{"123", "old_name1", OLD},
{"456", "new_name2", NEW},
{"456", "old_name1", OLD},
]
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers. I just want to "inject" each old employee under the new employee with the same personalNumber
You can do like this: As you mentioned that both lists have the same PersonalNumber so you can group by using this property. To ensure order based on the personalNumber, I've used LinkedHashMap.
Stream.concat(newList.stream(), oldList.stream())
.collect(Collectors.groupingBy(Employee::getPersonalNumber,
LinkedHashMap::new, Collectors.toList()))
.values().stream().flatMap(List::stream)
.collect(Collectors.toList());
Note: The result of stream#concat is ordered if both of the input streams are ordered.
Collections to sort should work for this.
newList.addAll(oldList);
Collections.sort(newList, Comparator.comparing(Employee::getPersonalNumber) );
The key is that, "This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort."
Since cannot sort the new list, I take that to mean you don't know the order of the new list. You can do it the ol' N^2 method.
for(int i = 0; i<newList.size(); i+=2){
String newNum = newList.get(i).getPersonalNumber();
Employee old = oldList.stream().filter(
emp->newNum.equals(
emp.getPersonalNumber()
)
).findFirst().orElse(null);
newList.add(i+1, old);
oldList.remove(old); //not nescessary?
}
Here is another approach with Java streams and InsStream
private List<Employee> mergeLists(List<Employee> oldList, List<Employee> newList) {
return IntStream.range(0, oldList.size())
.mapToObj(index -> Arrays.asList(newList.get(index), oldList.get(index)))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
This approach is possible because of
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers.
You can do it as follows:
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
Demo:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
enum Status {
OLD, NEW;
}
class Employee {
private String name;
private String personalNumber;
private Status status;
public Employee() {
super();
}
public Employee(String name, String personalNumber, Status status) {
this.name = name;
this.personalNumber = personalNumber;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPersonalNumber() {
return personalNumber;
}
public void setPersonalNumber(String personalNumber) {
this.personalNumber = personalNumber;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
#Override
public String toString() {
return personalNumber + ", " + name + ", " + status;
}
}
public class Main {
public static void main(String[] args) {
// old list
List<Employee> oldList = new ArrayList<Employee>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
// Display the result
result.forEach(System.out::println);
}
}
Output:
123, new_name1, NEW
123, old_name1, OLD
456, new_name2, NEW
456, old_name2, OLD
Related
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed last month.
Given the following task. We have an Employee and a Company classes. Each instance of Employee class is stored in array Employee[] employees in the Company class. I need a method which removes an instance of Employee in the array Employee[] employees by id.
I managed to write the following code:
public class Employee {
protected final int id;
protected String name;
public Employee(int id, String name) {
this.id = id;
this.name= name;
}
public int getId() {
return id;
}
}
public class Company {
private Employee[] employees;
private int size;
private static final int defaultCapacity = 5;
public Company() {
this(defaultCapacity);
}
public Company(int capacity) {
if (capacity <= 0)
throw new RuntimeException("capacity is required");
employees = new Employee[capacity];
}
public Employee removeEmployee(int id) {
Collection<Employee> employeeList = Arrays.asList(employees)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
Employee[] employeeArray = employeeList.toArray(Employee[]::new);
for (int i = 0; i < size; i++) {
if(employeeArray[i].getId() == id) {
Employee removedEmployee = employees[i];
employeeList.remove(employeeArray[i]);
employees = employeeList
.stream()
.filter(Objects::nonNull)
.toArray(Employee[]::new);
return removedEmployee;
}
}
return null;
}
}
The problem is that my method public Employee removeEmployee(int id) throws NullPointerException if an element for removal is not found.
Question:
How can I rewrite the method public Employee removeEmployee(int id) using, for instance, Streams API and Optional in oder to get rid of NullPointerException in the method public Employee removeEmployee(int id)?
N.B.: The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
There is a lot of ways to get rid of the NullPointerException here.
If you want to keep using the stream API, you may want to use filter and findAny.
For example, you could modify the method to the following:
public Employee removeEmployee(int id) {
Optional<Employee> employee = Arrays.stream(employees)
.filter(Objects::nonNull)
.filter(x -> x.getId() == id).
.findAny();
if(employee.isEmpty())
return null;
employees = Arrays.stream(employees).filter(x -> x != employee.get()).toArray(Employee[]::new);
return employee.get();
}
However, I would highly advise using a List or even a Map instead of an Array for employees as this makes things way easier and faster:
public Employee removeEmployee(int id){
Optional<Employee> toRemove = employees.stream().filter(x -> x.getId() == id).findAny();
if(toRemove.isEmpty())
return null;
employees.remove(toRemove.get());
return toRemove.get();
}
Or not to use the Stream API:
public Employee removeEmployee(int id){
int idx;
for(idx = 0; idx < employees.length; idx++){
if(employees[idx] != null && employees[idx].getId() == id)
break;
}
if(idx == employees.length)
return null;
Employee value = employees[idx];
Employee[] newArr = new Employee[employees.length - 1];
// the parameters here are left as an exercise to the reader :P
System.arraycopy(newArr, ...);
System.arraycopy(newArr, ...);
employees = newArr;
return value;
}
The length of the array Employee[] employees declared in the class Company must be reduced after the element has been successfully removed.
Streams doesn't buy you a lot in this case.
What you're supposed to do is to find the element with the target id, and if such an element exists, allocate a new array in memory with a length smaller by 1 copy all the elements apart from the one that was found, and assign employees with the reference to the new array.
To reduce the length, we can make use of the System.arraycopy(). First copy the elements before the target, and then after the target.
That's how it would look like with a plain index-based for-loop.
public Employee removeEmployee(int id) {
Employee result = null;
int index = -1;
for (int i = 0; i < employees.length; i++) {
if (employees[i] != null && employees[i].getId() == id) {
result = employees[i];
employees[i] = null;
break;
}
}
if (result != null) {
reduceLength(index);
}
return result;
}
public void reduceLength(int i) {
Employee[] newEmployees = new Employee[employees.length - 1];
System.arraycopy(employees, 0, newEmployees, 0, i);
System.arraycopy(employees, i + 1, newEmployees, i, employees.length - (i + 1));
employees = newEmployees;
}
If you want to do weird stuff and use Stream API and Optional at all costs, here how it can be done (but I would recommend to stick with the code above):
public Optional<Employee> removeEmployee(int id) {
Optional<Integer> index = IntStream.range(0, employees.length)
.filter(i -> employees[i] != null)
.filter(i -> employees[i].getId() == id)
.boxed() // otherwise will get OptionalInt which lacks map() method
.findFirst();
Optional<Employee> result = index.map(i -> employees[i]);
index.ifPresent(this::reduceLength);
return result;
}
Considering it's homework and constraints mentioned, i believe you are supposed to do all the work using the array only.
I'll provide some guideline and leave the actual implementation to you:
public class Company {
private Employee[] employees;
private int size;
public Employee removeEmployee(int id) {
int index = -1;
//find the index of employee with required id, you have mostly done that
if (index == -1) {
return null;
}
//save found employee to variable
//remove from array
//shift array to the left
//do not forget to use and reassign size variable where appropriate
}
//some extra
public void addEmployee(Employee employee) {
//resize array if necessary
//add employee at correct position in array
//do not forget to use and reassign size variable where appropriate
}
}
If you get stuck, you can look at the ArrayList class, your task is basically a simplified version of it. I strongly advise you to use this as source of inspiration only and not to copy the source code!!!
I have an array of Objects which includes products that has been sold and the amount of how many were sold in that one order. There may be one product appearing many times. I am trying to create a code, which would return the most popular product. e.g. Object1 10, Object 2 15, Object1 5, Object3 4 and it should return Object1 and the number 15 (10+5). Order has parameters product name and quantity, with getters and setters.
My idea was to use a set, which would get rid of all the duplicates (code example below), however it turned out to be a bust, since set would not work in this case and I wasn't able to even finish it with a set. I don't know what else to try. Thanks!
public class Orders {
private Product[] orders;
public Orders() {
orders = new order[0];
}
public void add(Order order) {
Order[] newOrder = Arrays.copyOf(orders,
orders.length + 1);
newOrder[newOrder.length - 1] = order;
orders = newOrders;
}
// the method described starts here
public Product findTopProduct() {
int[] array;
Set<String> set = new HashSet<>();
for (int i=0; i<orders.length; i++) {
set.add(orders[i].getProductName());
}
array = new int[set.size()];
for (int i=0; i<orders.length; i++) {
for (int i1=0; i1<set.size();i1++) {
}
}
}
}
Consider a little bit wider quest (find the max, min, average...). There is one powerful class called IntSummaryStatistics
You can try to understand it and "exstract" the desired data.
I'll give you an example how to use the Map collection and what would be the output for some sample data:
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.Map;
import java.util.stream.Collectors;
public class Answer {
static class Product{
private String name;
private int noOfSales;
public Product(String name, int noOfSales){
this.name = name;
this.noOfSales = noOfSales;
}
public String getName() {
return name;
}
public int getNoOfSales(){
return noOfSales;
}
}
public static void main(String[] args) {
Product[] products = {
new Product("tea", 4),
new Product("apple", 12),
new Product("tea", 15)
};
Map<String, IntSummaryStatistics> mapOfProducts =
Arrays.stream(products)
.collect(Collectors.groupingBy(
Product::getName,
Collectors.summarizingInt(Product::getNoOfSales)));
System.out.println(mapOfProducts);
}
}
The output is:
{apple=IntSummaryStatistics{count=1, sum=12, min=12, average=12.000000, max=12}, tea=IntSummaryStatistics{count=2, sum=19, min=4, average=9.500000, max=15}}
Please, notice that finding only maximum reducing method. (This contains an example code)
This is best addressed using Map data structure. You need to map each product name to its quantity across all orders you have. I'm using HashMap in Java, but you also need to understand the Map data structure and how/when to use it, here's the way I would implement it (I'm assuming you Product class has another property called quantity):
public Product findTopProduct() {
Map<String, Integer> map = new HashMap<>(); // this map should contain each product name pointing to its quantity across all orders (obj1 -> 10, obj2 -> 15, obj3 -> 4, ...)
for (int i=0; i<orders.length; i++) {
if(map.containsKey(orders[i].getProductName())) { // if it already exists in map, then you need to add the quantity only and not create a new entry
map.put(orders[i].getProductName(), map.get(orders[i].getProductName() + orders[i].getQuantity()));
} else { // add the productName maping to its quantity
map.put(orders[i].getProductName(), order[i].getQuantity);
}
}
String productName = null;
Integer max = null;
for(Map.Entry<String, Integer> entry : map.entrySet()) { // loop on map entries
if(productName == null && max == null) { // the first iteration they are both null
productName = entry.getKey();
max = entry.getValue();
continue;
}
if(entry.getValue() > max) { // maximize
max = entry.getValue();
productName = entry.getKey();
}
}
Product product = new Product(productName, max);
return product;
}
Try this:
public static Product findTopProduct(Product[] orders) {
Map<String,List<Product>> var0 = Stream.of(orders)
.collect(Collectors.groupingBy(Product::getName));
int maxCount = Integer.MIN_VALUE;
Product top = null;
for(Map.Entry<String,List<Product>>entry : var0.entrySet()) {
int size = entry.getValue().size();
if (size > maxCount) {
top = entry.getValue().get(0);
maxCount = size;
}
}
return top;
}
This getTopProduct method returns a new Product with the product name and total quantity.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class TestClass {
public static void main(String[] args) {
//sample list of objects containing productName and quantity fields
List<Product> productList = new ArrayList<>();
Product p1 = new Product("toy", 10);
Product p2 = new Product("car", 14);
Product p3 = new Product("food", 2);
Product p4 = new Product("toy", 6);
productList.add(p1);
productList.add(p2);
productList.add(p3);
productList.add(p4);
Product topProduct = getTopProduct(productList);
System.out.println("Most sold product: " + topProduct.getProductName() + ", " + topProduct.getQuantity());
}
private static Product getTopProduct(List<Product> productList) {
//map to hold products and total quantity
Map<String, Integer> map = new HashMap<>();
//populate the map
for(Product product : productList) {
if(map.containsKey(product.getProductName())) {
//if product already exists in map, add on the quantity
map.put(product.getProductName(), map.get(product.getProductName()) + product.getQuantity());
}else {
//if product doesnt exist in map yet, add it
map.put(product.getProductName(), product.getQuantity());
}
}
//find the highest value in the map which is the product with highestQuantity
int highestQuantity = Collections.max(map.values());
String productWithHighestQuantity = "";
//go through map to find which product had the highest quantity
for (Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue()==highestQuantity) {
productWithHighestQuantity = entry.getKey();
break;
}
}
return new Product(productWithHighestQuantity, highestQuantity);
}
}
For example, I have a list which contains some Lecture instances, each lecture has a certain number of students attending this lecture and another list which contains some Classroom instances, each classroom has a maximum capacity.
Now I intend to assign each lecture in lecture list with a classroom in classroom list, all lectures in lecture class should have a classroom, then create a map to store this possibility.
And I want to return all these possible matches in the form of a set.
For example:
Classroom List: [Classroom1(50),Classroom2(70),Classroom3(80)]
Lecture list: [Lecture1(50), Lecture2(70), Lecture3(50)]
Then we have 3 possible maps, which are:
{lecture1:classroom1, lecture2:classroom2, lecture3:classroom3} and
{lecture1:classroom1, lecture2:classroom3, lecture3:classroom2} and
{lecture1:classroom2, lecture2:classroom3, lecture3:classroom1}
After that, all possible maps should be stored in a set.
I am new to programming and has not learned algorithm yet, maybe that's why I'm so struggled on this, I'd be grateful if someone could help me solve this problem.
What you seem to be after is something known as the cartesian product.
See https://en.wikipedia.org/wiki/Cartesian_product
You can do this with Java 8 streams
All permutations
// Just substitute the types and values for Lecture and Classroom instances
// I'm not going to do this for you
final List<String> first = Arrays.asList("foo","bar","baz");
final List<String> second = Arrays.asList("spam","ham","eggs");
final Set<Map.Entry<String,String>> objects =
first
.stream()
.flatMap(f ->
second
.stream()
.map(s -> new AbstractMap.SimpleEntry<>(f, s)))
.collect(Collectors.toSet());
Your 'objects' set is going to contain Abstract entry maps which hold your combinations.
Set[
Map{foo : spam}
Map{foo : ham}
Map{foo : eggs}
Map{bar : spam}
Map{bar : ham}
Map{bar : eggs}
Map{baz : spam}
Map{baz : ham}
Map{baz : eggs}
]
Groups of combinations
If you actually want 3 items in your set, you can do an intermediate collect on the second stream to collect into a data structure of your choice. Below shows that for a list, as I've already shown use of Collectors.toSet()
final Set<List<AbstractMap.SimpleEntry<String,String>>> objects =
first
.stream()
.map(f ->
second
.stream()
.map(s -> new AbstractMap.SimpleEntry<>(f, s))
.collect(Collectors.toList()))
.collect(Collectors.toSet());
Your 'objects' set is going to contain a list of Abstract entry maps which hold your combinations.
Set[
List(
Map{foo : spam}, Map{foo : ham}, Map{foo : eggs}
),
List(
Map{bar : spam}, Map{bar : ham}, Map{bar : eggs}
),
List(
Map{baz : spam}, Map{baz : ham}, Map{baz : eggs}
)
]
This illustrates a simple cartesian product algorithm using Java 8 in a single functional statement. Should you wish to add any clauses or exclusions, you can use filter or any of the other higher order functions to manipulate the stream.
So i got sucked in to this one and wrote a working solution
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
class ClassMatcher {
//The set of all possible matchings.
static ArrayList<ArrayList<Pair>> set = new ArrayList<ArrayList<Pair>>();
// The current matching being built
static ArrayList<Pair> cur = new ArrayList<Pair>();
public static void main(String[] args) {
Lecture[] l = { new Lecture(50, 1), new Lecture(70, 2), new Lecture(50, 3)};
ArrayList<Classroom> c = new ArrayList<>(Arrays.asList(
new Classroom(50, 1), new Classroom(70, 2),
new Classroom(100, 3)));
for (int i = 0; i < l.length; i++) {
//Fill with dummy values
cur.add(new Pair(new Classroom(-1, -1), new Lecture(-1, -1)));
}
// Sort the arrays to save work in rec()
Arrays.sort(l);
//Sort classrooms in descending order
Collections.sort(c, new Comparator<Classroom>() {
#Override
public int compare(Classroom o1, Classroom o2) {
return o1.compareTo(o2) * -1;
}
});
recursive(l, c, 0);
// Print all the sets
for (int i = 0; i < set.size(); i++) {
System.out.print("{");
for (int j = 0; j < set.get(i).size(); j++) {
System.out.print("Lecture " + set.get(i).get(j).l + ": "
+ "Classroom " + set.get(i).get(j).c);
if (j < set.get(i).size() - 1) {
System.out.print(", ");
} else {
System.out.print("}");
}
}
System.out.println();
}
}
public static void recursive(Lecture[] lectureList,
ArrayList<Classroom> classroomList, int curLecture) {
for (int i = 0; i < classroomList.size(); i++) {
// if the classroom is smaller than the lecture we cna stop as the
// lists are sorted so all other lectures will be to big for the
// current classroom
if (lectureList[curLecture].size > classroomList.get(i).size) {
return;
}
//Match the current classroom to the current lecture and add to the working matching
cur.set(curLecture, new Pair(classroomList.get(i), lectureList[curLecture]));
//If there are more lectures to do then remove the used classroom and recursively call.
if (curLecture < lectureList.length - 1) {
Classroom tmp = classroomList.remove(i);
recursive(lectureList, classroomList, curLecture + 1);
classroomList.add(i, tmp);
}
// If no Lectures left then add this matching to the set of all matchings.
else {
ArrayList<Pair> copy = (ArrayList<Pair>) cur.clone();
set.add(copy);
}
}
}
}
class Classroom implements Comparable<Classroom> {
int size;
int number;
public Classroom(int s, int n) {
size = s;
number = n;
}
#Override
public int compareTo(Classroom o) {
return Integer.compare(this.size, o.size);
}
public String toString() {
return number + " (" + size + ")";
}
}
class Lecture implements Comparable<Lecture> {
int size;
int number;
public Lecture(int s, int n) {
size = s;
number = n;
}
#Override
public int compareTo(Lecture o) {
return Integer.compare(this.size, o.size);
}
public String toString() {
return number + " (" + size + ")";
}
}
class Pair {
Classroom c;
Lecture l;
public Pair(Classroom c, Lecture l) {
this.c = c;
this.l = l;
}
}
This gives the output
{Lecture 1 (50): Classroom 3 (100), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 2 (70), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 3 (100)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 3 (100), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 2 (70), Lecture 2 (70): Classroom 3 (100)}
Code below will give you all the matches, you can use them like whatever you want
HasMap<Integer, Integer> match = new HashMap<Integer, Integer>();
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(classroom[i] >= lecture[j]) {
match.add(lecture[j], classroom[i]);
}
}
}
if you want seperate maps like for every classroom or lecture you can try this(example for classrooms)
HashMap<Integer, Integer> classroom1 = new HashMap<Integer, Integer>();
HashMap<Integer, Integer> classroom2 = new HashMap<Integer, Integer>();
HashMap<Integer, Integer> classroom3 = new HashMap<Integer, Integer>();
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 0) {
if(classroom[i] >= lecture[j]) {
classroom1.add(lecture[j], classroom[i]);
}
}
if(i == 1) {
if(classroom[i] >= lecture[j]) {
classroom2.add(lecture[j], classroom[i]);
}
}
if(i == 2) {
if(classroom[i] >= lecture[j]) {
classroom3.add(lecture[j], classroom[i]);
}
}
}
}
After this you can create map of maps. Don't mind about correcting me or adding something. Have a good day!
The algorithm in itself might look something like this using an nested for each loop:
public Set<Object[]> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
Set<Object[]> returnSet = new LinkedHashSet<>();
for(Lecture l: lectures){
for (ClassRoom c: classRooms){
Object[] n = new Object[2];
n[0] = c;
n[1] = l;
returnSet.add(n);
}
}
return returnSet;
}
Here is an answer using Maps instead of arrays:
public Set<Map<Lecture, ClassRoom>> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
Set<Map<Lecture, ClassRoom>> returnSet = new LinkedHashSet<>();
for(Lecture l: lectures){
for (ClassRoom c: classRooms){
Map<Lecture, ClassRoom> n = new HashMap<>();
n.put(l,c);
}
}
return returnSet;
}
Here is an example with sorted data collections:
public Set<Map<Lecture, ClassRoom>> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
List<ClassRoom> sortedClassRooms = classRooms
.stream().sorted(Comparator.comparingInt(a -> a.getId())).collect(Collectors.toList());
List<Lecture> sortedLectures = lectures
.stream().sorted(Comparator.comparingInt(a -> a.getId())).collect(Collectors.toList());
Set<Map<Lecture, ClassRoom>> returnSet = new LinkedHashSet<>();
for(Lecture l: sortedLectures){
for (ClassRoom c: sortedClassRooms){
Map<Lecture, ClassRoom> n = new HashMap<>();
n.put(l,c);
}
}
return returnSet;
}
This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
How to sort Arraylist of objects
(3 answers)
Closed 9 years ago.
I m looking to sort an ArrayList which is of the type <String,int>, according to int.
So, my variable is var<String,int>
India 2
Pakistan 3
USA 1
The output becomes:
USA 1
India 2
Pakistan 3
I am confused how does it works with int. Collections.sort(var) does not works with it.
You can't use ArrayList of type
<String, int>
You can't have primitives in ArrayList as ArrayList holds objects. So, the closest you can do is to store Integer objects.
ArrayList can be of only one type if you are parameterizing it.
If you want to hold String and int, you can create a class CountryInfo with fields name and rank. Then create
ArrayList<CountryInfo> list =new ArrayList<CountryInfo>();
Then you can use
Collections.sort(list, <Comparator>)
I have created an example where you can sort your ArrayList even if its with objects. You can read through it an see if it's helps.
I have made two classes and a test class:
First class is Country:
public class Country {
private String countryName;
private int number;
public Country(String countryName, int number){
this.countryName = countryName;
this.number = number;
}
public String getCountryName(){
return countryName;
}
public void setCountryName(String newCountryName){
countryName = newCountryName;
}
public int getNumber(){
return number;
}
public void setNumber(int newNumber){
number = newNumber;
}
public String toString(){
return getCountryName() + getNumber();
}
}
Next class is Methods:
public class Methods {
private Country country;
private ArrayList<Country> overview = new ArrayList<Country>();
private ArrayList<Country> overviewSorted = new ArrayList<Country>();
int [] test;
public void regCountry(String countryname, int numbers){
if(!(countryname == "" && numbers == 0)){
overview.add(new Country(countryname, numbers));
} else {
System.out.println("The input was null");
}
}
public void showRegisteredCountries(){
if(!(overview.size() < 0)){
for(int i = 0; i < overview.size(); i++){
System.out.println("The country: " + overview.get(i).getCountryName() + " has the number: " + overview.get(i).getNumber() + " registered");
}
} else {
System.out.println("There are no country registered");
}
}
public void numbersOverFromArrayList(){
if(!(overview.size() < 0)){
test = new int [overview.size()];
for(int i = 0; i < overview.size(); i++){
test[i] = overview.get(i).getNumber();
}
}
}
public void sortArrayAndCopyItBack(){
if(!(test.length < 0)){
java.util.Arrays.sort(test);
for(int i = 0; i < test.length; i ++){
for(int j = 0; j < overview.size(); j++){
if(test[i] == overview.get(j).getNumber()){
overviewSorted.add(new Country(overview.get(j).getCountryName(), overview.get(j).getNumber()));
}
}
}
}
}
public void showTableSorted(){
if(!(overviewSorted.size() < 0)){
for(int i = 0; i < overviewSorted.size(); i++){
System.out.println("Country name: " + overviewSorted.get(i).getCountryName() + " with number: " + overviewSorted.get(i).getNumber());
}
} else {
System.out.println("There are non countrys in table that is sorted");
}
}
}
Next is the test class:
public class test2 {
public static void main(String [] args){
Methods methodes = new Methods();
for(int i = 0; i < 4; i++){
String inCountry = JOptionPane.showInputDialog("Country:");
String inNumber = JOptionPane.showInputDialog("number:");
String country = inCountry;
int number = Integer.parseInt(inNumber);
methodes.regCountry(country, number);
}
methodes.showRegisteredCountries();
methodes.numbersOverFromArrayList();
methodes.sortArrayAndCopyItBack();
methodes.showTableSorted();
}
}
My output:
The country: Norway has the number: 5 registered
The country: Sweden has the number: 2 registered
The country: Denmark has the number: 9 registered
The country: Finland has the number: 7 registered
Country name: Sweden with number: 2
Country name: Norway with number: 5
Country name: Finland with number: 7
Country name: Denmark with number: 9
That is not an ArrayList. Use TreeMap in Stead.
Map<String, Integer> countryInfo = new TreeMap<String,Integer>();
This way it will be sorted automatically
You can sort
use Collections.sort(list,Comparator implementation)
in the implementation(here I have used anonymous implementation) override compare method
where you
get last character of each string convert to string and compare them
ArrayList<String> a=new ArrayList<String>();
a.add("India 2");
a.add("Pakistan 3");
a.add("USA 1");
Collections.sort(a, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
Integer i=Integer.valueOf(o1.substring((o1.length() -1),o1.length()));
Integer j=Integer.valueOf(o2.substring((o2.length() -1),o2.length()));
return i.compareTo(j);
}
});
You can optimist code
ArrayList is a collection of one type of object. It is not like maps that can take two inputs.
Therefore, there are three options:
1. Make use of a TreeMap that contains both a Key and a Map and is automatically sorted by key or
2. Make use of an unsorted map and sort with a comparator - see Sort a Map<Key, Value> by values (Java) or
3. Use an arraylist of a custom class with a comparator.
-
1) Using a TreeMap
Treemaps are an implementation of red-black trees. See: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/TreeMap.html
TreeMap<Integer,String> countries = new TreeMap<Integer,String>();
countries.put(2, "India");
countries.put(1, "USA");
countries.put(3, "Pakistan");
Iterator<Entry<Integer, String>> it = countries.entrySet().iterator();
Entry<Integer, String> entry;
while(it.hasNext())
{
entry = it.next();
System.out.println(entry.getValue() + " " + entry.getKey());
}
And this Produces:
USA 1
India 2
Pakistan 3
-
2) Make use of an unsorted map and sort with a comparator
See: Sort a Map<Key, Value> by values (Java) as the answer is very will written.
-
3) Using an ArrayList with Country Class
In order to support your example you would need to create a Country class.
You would need to do the following:
Implement Comparable within your country class and place the logic for the comparison within there.
Create a custom comparator that you will give to your Collection.sort invocation.
import java.util.ArrayList;
import java.util.Collections;
import java.util.InputMismatchException;
import java.util.Iterator;
public class CountrySortExample {
public static void main(String[] args) {
new CountrySortExample();
}
public ArrayList<Country> countries = new ArrayList<Country>();
public CountrySortExample()
{
countries.add(new Country("India",2));
countries.add(new Country("Pakistan",3));
countries.add(new Country("USA",1));
Collections.sort(countries);
Iterator<Country> it = countries.iterator();
Country count;
while(it.hasNext())
{
count = it.next();
System.out.println(count.CountryName + " " + count.CountryIndex);
}
}
class Country implements Comparable
{
public String CountryName;
public int CountryIndex;
public Country(String CountryName,int CountryIndex )
{
this.CountryName = CountryName;
this.CountryIndex = CountryIndex;
}
#Override
public int compareTo(Object o) {
if(! (o instanceof Country))
throw new InputMismatchException("Country is expected");
Country other = (Country)o;
if(other.CountryIndex > CountryIndex)
return -1;
else if(other.CountryIndex == CountryIndex)
return 0;
else return 1;
}
}
}
Further information is available at: http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/
If you have an object that you want to sort in more than one way, you define a Comparator class for each type of sort you want to do.
Using the example that the OP gave, here's one way to define the object and Comparators.
Here's one test result:
CountryRating [name=India, rating=2]
CountryRating [name=Pakistan, rating=3]
CountryRating [name=USA, rating=1]
CountryRating [name=USA, rating=1]
CountryRating [name=India, rating=2]
CountryRating [name=Pakistan, rating=3]
And here's the example code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CountryRating {
private String name;
private int rating;
public CountryRating(String name, int rating) {
this.name = name;
this.rating = rating;
}
public String getName() {
return name;
}
public int getRating() {
return rating;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CountryRating [name=");
builder.append(name);
builder.append(", rating=");
builder.append(rating);
builder.append("]");
return builder.toString();
}
public static void main(String[] args) {
List<CountryRating> list = new ArrayList<CountryRating>();
CountryRating cr1 = new CountryRating("USA", 1);
CountryRating cr2 = new CountryRating("India", 2);
CountryRating cr3 = new CountryRating("Pakistan", 3);
list.add(cr1);
list.add(cr2);
list.add(cr3);
Collections.sort(list, new CountrySort());
printList(list);
System.out.println(" ");
Collections.sort(list, new RatingSort());
printList(list);
}
private static void printList(List<CountryRating> list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
class CountrySort implements Comparator<CountryRating> {
#Override
public int compare(CountryRating cr1, CountryRating cr2) {
return cr1.getName().compareTo(cr2.getName());
}
}
class RatingSort implements Comparator<CountryRating> {
#Override
public int compare(CountryRating cr1, CountryRating cr2) {
return cr1.getRating() - cr2.getRating();
}
}
I have two List of array string. I want to be able to create a New List (newList) by combining the 2 lists. But it must meet these 3 conditions:
1) Copy the contents of store_inventory into newList.
2) Then if the item names in store_inventory & new_acquisitions match, just add the two quantities together and change it in newList.
3) If new_acquisitions has a new item that does not exist in store_inventory, then add it to the newList.
The titles for the CSV list are: Item Name, Quantity, Cost, Price.
The List contains an string[] of item name, quantity, cost and price for each row.
CSVReader from = new CSVReader(new FileReader("/test/new_acquisitions.csv"));
List <String[]> acquisitions = from.readAll();
CSVReader to = new CSVReader(new FileReader("/test/store_inventory.csv"));
List <String[]> inventory = to.readAll();
List <String[]> newList;
Any code to get me started would be great! =]
this is what i have so far...
for (int i = 0; i < acquisitions.size(); i++) {
temp1 = acquisitions.get(i);
for (int j = 1; j < inventory.size(); j++) {
temp2 = inventory.get(j);
if (temp1[0].equals(temp2[0])) {
//if match found... do something?
//break out of loop
}
}
//if new item found... do something?
}
I would start by building the newList as a HashMap or TreeMap instead of a List. This makes it easy to search for the matching record. Furthermore, I would convert the String[] to a custom object (e.g. Record) that contains the name, quantity, cost and price field. This would take care of copying the information. The you could try something like this:
Map<String, Record> newMap = new TreeMap<String, Record>();
for(String[] ss : acquisitions) {
Record rec = Record.parse(ss); // For requirement (1)
newMap.put(rec.getName(), rec);
}
for(String[] ss : inventory) {
Record rec = Record.parse(ss); // For requirement (1)
if(newMap.containsKey(rec.getName())) {
// For requirement (2)
// The mergeWith method can then add quantities together
newMap.get(rec.getName()).mergeWith(rec);
} else {
// For requirement (3)
newMap.put(rec.getName(), rec);
}
}
edit
An extra advantage of having a Record object, is that it can be printed to screen much easier by implementing the toString function.
public class Record implements Comparable<Record> {
public static Record parse(String[] ss) {
// TODO: implement some basic parsing
}
private String name;
private int quantity;
private BigDecimal cost, price;
private Record() {}
public String getName() { return name; }
public int getQuantity() { return quantity; }
public BigDecimal getCost() { return cost; }
public BigDecimal getPrice() { return price; }
public int compareTo(Record other) {
return this.name.compareTo(other.name);
}
public String toString() {
return name;
}
}