Related
I have array of objects person (int age; String name;).
How can I sort this array alphabetically by name and then by age?
Which algorithm would you use for this ?
You can use Collections.sort as follows:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons> is now sorted by name, then by age.
String.compareTo "Compares two strings lexicographically" - from the docs.
Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.
For those able to use the Java 8 streaming API, there is a neater approach that is well documented here:
Lambdas and sorting
I was looking for the equivalent of the C# LINQ:
.ThenBy(...)
I found the mechanism in Java 8 on the Comparator:
.thenComparing(...)
So here is the snippet that demonstrates the algorithm.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Check out the link above for a neater way and an explanation about how Java's type inference makes it a bit more clunky to define compared to LINQ.
Here is the full unit test for reference:
#Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* #param name The name of the person.
* #param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
#Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Using the Java 8 Streams approach, with method references on the getters...
// Create a stream...
var sortedList = persons.stream()
// sort it (does not sort the original list)...
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// and collect to a new list
.collect(Collectors.toList());
Collection to an array ist also possible:
persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
.toArray(String[]::new);
And the Java 8 Lambda approach...
//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
if (p1.getName().compareTo(p2.getName()) == 0) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
});
Lastly...
// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
You need to implement your own Comparator, and then use it: for example
Arrays.sort(persons, new PersonComparator());
Your Comparator could look a bit like this:
public class PersonComparator implements Comparator<? extends Person> {
public int compare(Person p1, Person p2) {
int nameCompare = p1.name.compareToIgnoreCase(p2.name);
if (nameCompare != 0) {
return nameCompare;
} else {
return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
}
}
}
The comparator first compares the names, if they are not equals it returns the result from comparing them, else it returns the compare result when comparing the ages of both persons.
This code is only a draft: because the class is immutable you could think of building an singleton of it, instead creating a new instance for each sorting.
Have your person class implement Comparable<Person> and then implement the compareTo method, for instance:
public int compareTo(Person o) {
int result = name.compareToIgnoreCase(o.name);
if(result==0) {
return Integer.valueOf(age).compareTo(o.age);
}
else {
return result;
}
}
That will sort first by name (case insensitively) and then by age. You can then run Arrays.sort() or Collections.sort() on the collection or array of Person objects.
Guava's ComparisonChain provides a clean way of doing it. Refer to this link.
A utility for performing a chained comparison statement. For example:
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}
You can do like this:
List<User> users = Lists.newArrayList(
new User("Pedro", 12),
new User("Maria", 10),
new User("Rafael",12)
);
users.sort(
Comparator.comparing(User::getName).thenComparing(User::getAge)
);
I would be careful when using Guava's ComparisonChain because it creates an instance of it per element been compared so you would be looking at a creation of N x Log N comparison chains just to compare if you are sorting, or N instances if you are iterating and checking for equality.
I would instead create a static Comparator using the newest Java 8 API if possible or Guava's Ordering API which allows you to do that, here is an example with Java 8:
import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;
private static final Comparator<Person> COMPARATOR = Comparator
.comparing(Person::getName, nullsLast(naturalOrder()))
.thenComparingInt(Person::getAge);
#Override
public int compareTo(#NotNull Person other) {
return COMPARATOR.compare(this, other);
}
Here is how to use the Guava's Ordering API: https://github.com/google/guava/wiki/OrderingExplained
Create as many comparators as necessary. After, call the method "thenComparing" for each order category. It's a way of doing by Streams. See:
//Sort by first and last name
System.out.println("\n2.Sort list of person objects by firstName then "
+ "by lastName then by age");
Comparator<Person> sortByFirstName
= (p, o) -> p.firstName.compareToIgnoreCase(o.firstName);
Comparator<Person> sortByLastName
= (p, o) -> p.lastName.compareToIgnoreCase(o.lastName);
Comparator<Person> sortByAge
= (p, o) -> Integer.compare(p.age,o.age);
//Sort by first Name then Sort by last name then sort by age
personList.stream().sorted(
sortByFirstName
.thenComparing(sortByLastName)
.thenComparing(sortByAge)
).forEach(person->
System.out.println(person));
Look: Sort user defined object on multiple fields – Comparator (lambda stream)
Use Comparator and then put objects into Collection, then Collections.sort();
class Person {
String fname;
String lname;
int age;
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public Person(String fname, String lname, int age) {
this.fname = fname;
this.lname = lname;
this.age = age;
}
#Override
public String toString() {
return fname + "," + lname + "," + age;
}
}
public class Main{
public static void main(String[] args) {
List<Person> persons = new java.util.ArrayList<Person>();
persons.add(new Person("abc3", "def3", 10));
persons.add(new Person("abc2", "def2", 32));
persons.add(new Person("abc1", "def1", 65));
persons.add(new Person("abc4", "def4", 10));
System.out.println(persons);
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person t, Person t1) {
return t.getAge() - t1.getAge();
}
});
System.out.println(persons);
}
}
Or you can exploit the fact that Collections.sort() (or Arrays.sort()) is stable (it doesn't reorder elements that are equal) and use a Comparator to sort by age first and then another one to sort by name.
In this specific case this isn't a very good idea but if you have to be able to change the sort order in runtime, it might be useful.
You can use generic serial Comparator to sort collections by multiple fields.
import org.apache.commons.lang3.reflect.FieldUtils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* #author MaheshRPM
*/
public class SerialComparator<T> implements Comparator<T> {
List<String> sortingFields;
public SerialComparator(List<String> sortingFields) {
this.sortingFields = sortingFields;
}
public SerialComparator(String... sortingFields) {
this.sortingFields = Arrays.asList(sortingFields);
}
#Override
public int compare(T o1, T o2) {
int result = 0;
try {
for (String sortingField : sortingFields) {
if (result == 0) {
Object value1 = FieldUtils.readField(o1, sortingField, true);
Object value2 = FieldUtils.readField(o2, sortingField, true);
if (value1 instanceof Comparable && value2 instanceof Comparable) {
Comparable comparable1 = (Comparable) value1;
Comparable comparable2 = (Comparable) value2;
result = comparable1.compareTo(comparable2);
} else {
throw new RuntimeException("Cannot compare non Comparable fields. " + value1.getClass()
.getName() + " must implement Comparable<" + value1.getClass().getName() + ">");
}
} else {
break;
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
}
Arrays.sort(persons, new PersonComparator());
import java.util.Comparator;
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
if(null == o1 || null == o2 || null == o1.getName() || null== o2.getName() ){
throw new NullPointerException();
}else{
int nameComparisonResult = o1.getName().compareTo(o2.getName());
if(0 == nameComparisonResult){
return o1.getAge()-o2.getAge();
}else{
return nameComparisonResult;
}
}
}
}
class Person{
int age; String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Updated version:
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName());
return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult;
}
}
For a class Book like this:
package books;
public class Book {
private Integer id;
private Integer number;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "book{" +
"id=" + id +
", number=" + number +
", name='" + name + '\'' + '\n' +
'}';
}
}
sorting main class with mock objects
package books;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
Book b = new Book();
Book c = new Book();
Book d = new Book();
Book e = new Book();
Book f = new Book();
Book g = new Book();
Book g1 = new Book();
Book g2 = new Book();
Book g3 = new Book();
Book g4 = new Book();
b.setId(1);
b.setNumber(12);
b.setName("gk");
c.setId(2);
c.setNumber(12);
c.setName("gk");
d.setId(2);
d.setNumber(13);
d.setName("maths");
e.setId(3);
e.setNumber(3);
e.setName("geometry");
f.setId(3);
f.setNumber(34);
b.setName("gk");
g.setId(3);
g.setNumber(11);
g.setName("gk");
g1.setId(3);
g1.setNumber(88);
g1.setName("gk");
g2.setId(3);
g2.setNumber(91);
g2.setName("gk");
g3.setId(3);
g3.setNumber(101);
g3.setName("gk");
g4.setId(3);
g4.setNumber(4);
g4.setName("gk");
List<Book> allBooks = new ArrayList<Book>();
allBooks.add(b);
allBooks.add(c);
allBooks.add(d);
allBooks.add(e);
allBooks.add(f);
allBooks.add(g);
allBooks.add(g1);
allBooks.add(g2);
allBooks.add(g3);
allBooks.add(g4);
System.out.println(allBooks.size());
Collections.sort(allBooks, new Comparator<Book>() {
#Override
public int compare(Book t, Book t1) {
int a = t.getId()- t1.getId();
if(a == 0){
int a1 = t.getNumber() - t1.getNumber();
return a1;
}
else
return a;
}
});
System.out.println(allBooks);
}
}
I'm not sure if it's ugly to write the compartor inside the Person class in this case. Did it like this:
public class Person implements Comparable <Person> {
private String lastName;
private String firstName;
private int age;
public Person(String firstName, String lastName, int BirthDay) {
this.firstName = firstName;
this.lastName = lastName;
this.age = BirthDay;
}
public int getAge() {
return age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public int compareTo(Person o) {
// default compareTo
}
#Override
public String toString() {
return firstName + " " + lastName + " " + age + "";
}
public static class firstNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.firstName.compareTo(o2.firstName);
}
}
public static class lastNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.lastName.compareTo(o2.lastName);
}
}
public static class ageComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.age - o2.age;
}
}
}
public class Test {
private static void print() {
ArrayList<Person> list = new ArrayList();
list.add(new Person("Diana", "Agron", 31));
list.add(new Person("Kay", "Panabaker", 27));
list.add(new Person("Lucy", "Hale", 28));
list.add(new Person("Ashley", "Benson", 28));
list.add(new Person("Megan", "Park", 31));
list.add(new Person("Lucas", "Till", 27));
list.add(new Person("Nicholas", "Hoult", 28));
list.add(new Person("Aly", "Michalka", 28));
list.add(new Person("Adam", "Brody", 38));
list.add(new Person("Chris", "Pine", 37));
Collections.sort(list, new Person.lastNameComperator());
Iterator<Person> it = list.iterator();
while(it.hasNext())
System.out.println(it.next().toString());
}
}
I have 2 ArrayList. And I want elements with common phone.
ArrayList<Contact> phone_contacts;
ArrayList<Contact> registered_users;
I used below method to get common elements:
ArrayList<Contact> common_contacts = new ArrayList<Contact>(phone_contacts);
common_contacts.retainAll(registered_users);
But, the result I get is empty. How can I get common phone contact in common_contacts ArrayList?
Contact
public class Contact {
private String name;
private String phone;
public Contact(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
First, if you are treating a phone number as an identifier, I would advise caution. Falsehoods Programmers Believe About Phone Numbers
With that said...
retainAll ultimately uses the equals method. Given that you have a custom object (and not something like a String or int that have a defined equals), the simplest method would be to define an equals method for Contact that returns true if the two have the same phone number.
However, that might not be what you are looking for. For example, equals might need to check the name in other contexts.
There are a couple other approaches you could take. Since you mentioned Android, Java 8 streams are currently out. An Iterator loop might do the job. Collect all of the phone numbers for registered users into a Set (so you have the unique list), then start with a List of all of your contacts, and remove any that don't have a phone number from that set.
Set<String> registeredPhoneNumbers = new HashSet<>();
for (Contact c : registered_users) {
registeredPhoneNumbers.add(c.getPhone());
}
List<Contact> common_contacts = new ArrayList<>(phone_contacts);
for (Iterator<Contact> iter = common_contacts.iterator(); iter.hasNext();) {
Contact c = iter.next();
if (!registeredPhoneNumbers.contains(c.getPhone())) {
iter.remove();
}
}
Since you mentioned in comments that there could be a million distinct registered_users, this might be more space-efficient:
Set<String> phoneNumbers = new HashSet<>();
for (Contact c : phone_contacts) {
phoneNumbers.add(c.getPhone());
}
Set<String> overlappingNumbers = new HashSet<>();
for (Contact registered : registered_users) {
if (phoneNumbers.contains(registered.getPhone())) {
overlappingNumbers.add(registered.getPhone());
}
}
List<Contact> common_contacts = new ArrayList<>();
for (Contact contact : phone_contacts) {
if (overlappingNumbers.contains(contact.getPhone())) {
common_contacts.add(contact);
}
}
}
You may want to check the phone number for null as well.
Add these lines to your Contact class:
#Override
public boolean equals(Object obj) {
return obj instanceof Contact &&
this.name != null && this.name.equals(((Contact)obj).name) &&
this.phone!= null && this.phone.equals(((Contact)obj).phone);
}
Ref: https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-
See Also: hashCode()
If you use the method RetainAll in the List the you will get the common objects between 2 Lists..\
Example:
consider the lists of integers, (just for the sake of the example) it will work with your class...
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(0, 1, 2, 3, 4, 5));
List<Integer> list2 = new ArrayList<Integer>(Arrays.asList(1, 3, 5));
List<Integer> list3 = new ArrayList<Integer>(list1);
list3.retainAll(list2);
System.out.println("List1:" + list1);
System.out.println("List2:" + list2);
System.out.println("List common:" + list3);
}
In your case the class contacts needs to be modified so the Method ArrayLst.retainAll() can somehow identify whether a Contact is the same as the other using as criteria the Phone number...
Modify/Improve the Contact Class by adding the HashCode and Equals:
but you need to use as criteria only the phone Number
public class Contact {
private String name;
private int phone;
public Contact(String name, int phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Contact [name=" + name + ", phone=" + phone + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + phone;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Contact other = (Contact) obj;
if (phone != other.phone)
return false;
return true;
}
public int getPhone() {
return phone;
}
public void setPhone(int phone) {
this.phone = phone;
}
}
Implement the List of Contacts and call the method RetainAll
public static void main(String[] args) {
List<Contact> list1 = new ArrayList<Contact>(Arrays.asList(new Contact(UUID.randomUUID().toString(), 1),
new Contact(UUID.randomUUID().toString(), 2), new Contact(UUID.randomUUID().toString(), 3),
new Contact(UUID.randomUUID().toString(), 4), new Contact(UUID.randomUUID().toString(), 5)));
List<Contact> list2 = new ArrayList<Contact>(Arrays.asList(new Contact(UUID.randomUUID().toString(), 1),
new Contact(UUID.randomUUID().toString(), 3), new Contact(UUID.randomUUID().toString(), 5)));
List<Contact> list3 = new ArrayList<Contact>(list1);
list3.retainAll(list2);
System.out.println("List1:" + list1);
System.out.println("List2:" + list2);
System.out.println("List common:" + list3);
}
Java ArrayList, filled with objects called packinglistrows which hold three key values ( ISBN, PalletNumber, Quantity), along with other properties.
I have this ArrayList with all the same ISBN values in it. I want to be able to merge the items with the same PalletNumbers quantity value.
For example:
ArrayList items = [ packinglistrow( 1234, 1, 10 ), packinglistrow( 1234, 2, 5), packinglistrow( 1234, 1, 15 ) ]
After merge the [0] and [2] objects are merged as they have the same ISBN and pallet number 1. Resulting in a merged object with the updated quantity:
ArrayList items = [ packinglistrow( 1234, 1, 25 ), packinglistrow( 1234, 2, 5) ]
Was thinking loop over and compare and add the different types to new ArrayList then loop over and merge, but there must be a neater way of doing this?
Thanks.
First create a class to handle this datas. There's two important points to note. The equals and hashcode method are only based with the isbn and palletNumber values and there is a merge method that returns a new instance of PackingListRow with the quantities between this and an other instance you give as parameter.
class PackingListRow {
private final String isbn;
private final int palletNumber;
private final int quantity;
public PackingListRow(String isbn, int palletNumber, int quantity) {
this.isbn = isbn;
this.palletNumber = palletNumber;
this.quantity = quantity;
}
public String getIsbn() {
return isbn;
}
public int getPalletNumber() {
return palletNumber;
}
public int getQuantity() {
return quantity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackingListRow that = (PackingListRow) o;
return Objects.equals(palletNumber, that.palletNumber) &&
Objects.equals(isbn, that.isbn);
}
#Override
public int hashCode() {
return Objects.hash(isbn, palletNumber);
}
#Override
public String toString() {
return "PackingListRow{" +
"isbn='" + isbn + '\'' +
", palletNumber=" + palletNumber +
", quantity=" + quantity +
'}';
}
public PackingListRow merge(PackingListRow other) {
assert(this.equals(other));
return new PackingListRow(this.isbn, this.palletNumber, this.quantity + other.quantity);
}
}
Once you have that, you just need to create another new list that is initially empty. It will contains the merged values. For each instance in the initial list, you check whether it is already in the merged list. If yes, you modify the existing instance by calling merge, otherwise you just append it to the list. We end up with the following algorithm:
List<PackingListRow> list =
Arrays.asList(new PackingListRow("1234", 1, 10), new PackingListRow("1234", 2, 5), new PackingListRow("1234", 1, 15));
List<PackingListRow> mergedList = new ArrayList<>();
for(PackingListRow p : list) {
int index = mergedList.indexOf(p);
if(index != -1) {
mergedList.set(index, mergedList.get(index).merge(p));
} else {
mergedList.add(p);
}
}
System.out.println(mergedList);
Which outputs:
[PackingListRow{isbn='1234', palletNumber=1, quantity=25}, PackingListRow{isbn='1234', palletNumber=2, quantity=5}]
With Java 8, I would maybe use a different strategy (at least you show there are multiple ways to solve the problem). I would create a static class that does the grouping for me:
class PackingListRow {
private final String isbn;
private final int palletNumber;
private final int quantity;
static class GroupPacking {
private final String isbn;
private final int palletNumber;
public GroupPacking(PackingListRow p) {
this.isbn = p.isbn;
this.palletNumber = p.palletNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GroupPacking that = (GroupPacking) o;
return Objects.equals(palletNumber, that.palletNumber) &&
Objects.equals(isbn, that.isbn);
}
#Override
public int hashCode() {
return Objects.hash(isbn, palletNumber);
}
}
....
public PackingListRow merge(PackingListRow other) {
assert (new GroupPacking(other).equals(new GroupPacking(this)));
return new PackingListRow(this.isbn, this.palletNumber, this.quantity + other.quantity);
}
}
Then you can use the Stream API. Given the original list, you get a Stream<PackingListRow> from which you collect the elements into a Map according by their GroupPacking instances (the keys). The value is simply the current PackingListRow instance. If you have two instances with the same GroupPacking value (according to equals/hashcode), you merge them. You finally get the values() of the map.
List<PackingListRow> mergedList =
new ArrayList<>(list.stream().collect(toMap(PackingListRow.GroupPacking::new, p -> p, PackingListRow::merge)).values());
Here you have working example (Java8 + Google Guava):
package com.rgrebski.test;
import com.google.common.base.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimaps;
import org.assertj.core.api.Assertions;
import org.testng.annotations.*;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PackingListRowMergeTest {
#Test
public void test() {
//given
List<PackingListRow> packingListRow = ImmutableList.of(
new PackingListRow(1234, 1, 10),
new PackingListRow(1234, 2, 5),
new PackingListRow(1234, 1, 15)
);
//when
List<PackingListRow> mergedPackingListRow = Multimaps.index(packingListRow, groupByPaletNumbers())
.asMap()
.values()
.stream()
.map(packingListRowCollectionToSinglePackingListRowWithQuantitySum())
.collect(Collectors.toList());
//then
Assertions.assertThat(mergedPackingListRow).containsExactly(
new PackingListRow(1234, 1, 25),
new PackingListRow(1234, 2, 5)
);
}
protected java.util.function.Function<Collection<PackingListRow>, PackingListRow> packingListRowCollectionToSinglePackingListRowWithQuantitySum() {
return new java.util.function.Function<Collection<PackingListRow>, PackingListRow>() {
#Override
public PackingListRow apply(Collection<PackingListRow> packingListRows) {
int quantitySum = packingListRows.stream().flatMapToInt(packingListRow -> IntStream.of(packingListRow.getQuantity())).sum();
PackingListRow firstPackingListRow = packingListRows.stream().findFirst().get();
return new PackingListRow(firstPackingListRow.getIsbn(), firstPackingListRow.getPaletNumber(), quantitySum);
}
};
}
private Function<? super PackingListRow, Integer> groupByPaletNumbers() {
return new Function<PackingListRow, Integer>() {
#Override
public Integer apply(PackingListRow input) {
return input.getPaletNumber();
}
};
}
private static class PackingListRow {
private int isbn;
private int paletNumber;
private int quantity;
public PackingListRow(int isbn, int paletNumber, int quantity) {
this.isbn = isbn;
this.paletNumber = paletNumber;
this.quantity = quantity;
}
public int getIsbn() {
return isbn;
}
public int getPaletNumber() {
return paletNumber;
}
public int getQuantity() {
return quantity;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackingListRow that = (PackingListRow) o;
return Objects.equal(this.isbn, that.isbn) &&
Objects.equal(this.paletNumber, that.paletNumber) &&
Objects.equal(this.quantity, that.quantity);
}
#Override
public int hashCode() {
return Objects.hashCode(isbn, paletNumber, quantity);
}
#Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("isbn", isbn)
.add("paletNumber", paletNumber)
.add("quantity", quantity)
.toString();
}
}
}
It seems reasonable to create an object just for ISBN & pallet number, but keep the quantity separately as you consider them equal if ISBN & pallet number are equal. So the object might look like this:
public class PackingListRow {
private final String isbn;
private final int palletNumber;
public PackingListRow(String isbn, int palletNumber) {
this.isbn = isbn;
this.palletNumber = palletNumber;
}
#Override
public int hashCode() {
return palletNumber * 31 + ((isbn == null) ? 0 : isbn.hashCode());
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PackingListRow other = (PackingListRow) obj;
if (isbn == null) {
if (other.isbn != null)
return false;
} else if (!isbn.equals(other.isbn))
return false;
if (palletNumber != other.palletNumber)
return false;
return true;
}
#Override
public String toString() {
return isbn+":"+palletNumber;
}
}
After that you can store the results in the Map object and add items via method like this:
public static void addItem(Map<PackingListRow, Integer> items, PackingListRow row,
int quantity) {
Integer oldQuantity = items.get(row);
items.put(row, oldQuantity == null ? quantity : quantity+oldQuantity);
}
Or much simpler if you are using Java 8:
public static void addItem(Map<PackingListRow, Integer> items, PackingListRow row,
int quantity) {
items.merge(row, quantity, Integer::sum);
}
Testing on your sample data:
public static void main(String[] args) {
Map<PackingListRow, Integer> items = new HashMap<PackingListRow, Integer>();
addItem(items, new PackingListRow("1234", 1), 10);
addItem(items, new PackingListRow("1234", 2), 5);
addItem(items, new PackingListRow("1234", 1), 15);
System.out.println(items);
}
The output is:
{1234:1=25, 1234:2=5}
I'm having troubling organizing my list of employees. I just need to organize them according to their employee type (first two letters). Each object starts with the employee code which are the first two letters. This is what I need to separate the position types but for some reason I can't grab them.
Here is the file that I am creating the objects out of and storing them in the arrays.
PW_1234,James,Bond,01/02/10,1,10 PW_1235,John,Brown,02/03/10,2,10.5
PW_1236,Howard,Johnson,03/04/10,3,11
PW_1237,Francis,Themule,04/05/11,4,10.75
PW_1238,Mathew,Lewis,05/06/11,1,12.75
PW_1239,Mark,Bixton,05/13/11,2,13
PW_1242,Sarah,Glover,05/14/11,1,13.75 PW_1245,John,Doe,05/15/11,4,10.5
PW_1245,Mary,Doe,05/15/11,4,10.5
TL_1248,Abel,English,05/16/11,3,16.5,0.01,100,89
TL_1251,Eustis,Clauser,05/17/11,2,16,0.02,100,9
SU_1254,Henry,Hollowman,05/18/11,1,40000,0.01
PW_1240,Luke,Sailor,01/22/12,3,14.5 PW_1243,Jane,Baker,01/23/12,2,14
PW_1243,Jane,Baker,01/23/12,2,14
TL_1246,David,Brief,01/24/12,1,14.75,0.01,100,57
PW_1246,David,Doson,01/24/12,1,14.75
TL_1249,Baker,Anderson,01/25/12,4,11.5,0.01,100,100
TL_1252,Frank,Donson,01/26/12,3,17.5,0.02,100,39
SU_1255,Issac,Asimov,01/27/12,2,43000,0.02
SU_1256,Issac,Shoreman,01/28/12,3,39000,0.01
SU_1257,Issac,Roberts,01/29/12,4,35500,0.01
PW_1241,John,Candy,11/23/13,4,9.5 PW_1244,Kate,Smith,11/24/13,3,15.5
PW_1244,Kate,Handle,11/24/13,3,15.5
TL_1247,Samual,Dempky,11/25/13,2,15,0.01,100,10
TL_1250,Charley,Boman,11/26/13,1,15.75,0.01,100,50
TL_1253,George,Fritzmen,11/27/13,4,12.5,0.02,100,27
Here is the code:
private String makeEmployeeList()
{
String list = "";
for(int i=0; i < employees.length; i++)
{
list += "\n"+employees[i].toString();
if(employees[i]substring(0,2).equals("SU"))
{
list += "\n"+employees[i].toString();
}
}
return list;
}
**Here is how the employees array is created:
private Employee[] employees;
**Here is how everything is loaded into it.
public void loadEmployeesFromFile(String fileName)
{
File inFile = new File(fileName);
if(inFile.exists()) // MAKE SURE FILE EXISTS
{
try
{
BufferedReader inReader = new BufferedReader(new FileReader(inFile));
inReader.mark(32000);
String inLine = inReader.readLine();
//************************************
// Counting rows to set array size
//************************************
int rowCount = 0;
while (inLine != null && !inLine.equals(""))
{
rowCount++;
inLine = inReader.readLine();
}
inReader.reset();
//*******************
// re-reading data
//*******************
this.employees = new Employee[rowCount];
for(int rowIndex = 0;rowIndex < rowCount; rowIndex++)
{
inLine = inReader.readLine();
Scanner employeeScanner = new Scanner(inLine).useDelimiter(",");
String workerType = employeeScanner.next();
String firstName = employeeScanner.next();
String lastName = employeeScanner.next();
String hireDate = employeeScanner.next();
int shift = employeeScanner.nextInt();
if(workerType.substring(0,2).equals("PW"))
{
double pay = employeeScanner.nextDouble();
employees[rowIndex]= new ProductionWorker(workerType, firstName, lastName, hireDate, shift, pay);
}
else if(workerType.substring(0,2).equals("TL"))
{
double pay = employeeScanner.nextDouble();
double bonusRate = employeeScanner.nextDouble();
int reqHours = employeeScanner.nextInt();
int recHours = employeeScanner.nextInt();
employees[rowIndex]= new TeamLeader(workerType, firstName, lastName, hireDate, shift, pay, bonusRate, reqHours, recHours);
}
else if(workerType.substring(0,2).equals("SU"))
{
double salary = employeeScanner.nextDouble();
double bonusRate = employeeScanner.nextDouble();
employees[rowIndex]= new ShiftSupervisor(workerType, firstName, lastName, hireDate, shift, salary, bonusRate );
}
}
return;
}catch(IOException ioe)
{
System.err.print("\nTrouble reading employee file: "+fileName);
}
}
JOptionPane.showMessageDialog(null, "\nFile Name does not exist!\n Process terminating!");
System.exit(0);
}
private String makeEmployeeList(){
StringBuilder sbSU = null;
for(int i=0; i < employees.length; i++)
{
sbSU = new StringBuilder();
if(employees[i].substring(0,2).equals("SU"))
{
sbSU.append(employees[i].toString());
}
}
return sbSU.toString();
}
First of all, you missed a dot after emplyees[i] subsrting
As string is an immutable object, I suggest you use StringBuilder and its append method instead of +=. and use its toString() method to convert StringBuilder to a String. You also need to override your Employees's toString method.
to sort the employees in an array, you need to implements Comparable or Comparator interface so that the Array knows which criteria to use when sorting your employees, in your case it is to compare the employee's type
As you are using JOptionPane you can use html inside to give it format. Make an Employee class and make it's natural order by type, or you can use a Comparator if you don't want to use Comparable
I made a complete example for you.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;
public class Employee implements Comparable<Employee> {
private String type;
private String name;
public Employee(String type, String name) {
super();
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.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;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
public int compareTo(Employee o) {
if (this.type.equals(o.type)) {
return name.compareTo(o.name);
}
return type.compareTo(o.type);
}
public static void main(String[] args) {
List<Employee> employees = new ArrayList<Employee>();
employees.add(new Employee("CA","John"));
employees.add(new Employee("CA", "Suzy"));
employees.add(new Employee("TA","Malcom"));
employees.add(new Employee("AA","Rose"));
// Sort the list by type as its natural order or use proper Comparator
Collections.sort(employees);
StringBuilder sb = new StringBuilder();
sb.append("<html><table><tr><td>Type</td><td>Name</td></tr>");
for (Employee e : employees) {
sb.append("<tr>");
sb.append("<td> ").append(e.getType()).append("</td>");
sb.append("<td> ").append(e.getName()).append("</td>");
sb.append("</tr>");
}
sb.append("</table></html>");
JOptionPane.showMessageDialog(null, sb);
}
}
Output:
I've User object a shown below:
User.java:
public class User {
public String firstName;
public String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Override
public int hashCode() {
return (this.firstName.hashCode() + this.lastName.hashCode());
}
#Override
public boolean equals(Object obj) {
if(obj instanceof User) {
User temp = (User) obj;
if(this.firstName.equals(temp.firstName) && this.lastName.equals(temp.lastName)) {
return true;
}
}
return false;
}
}
And main program is shown below:
import java.util.*;
class pp {
public static void main(String[] args) {
List<User[]> a = new ArrayList<User[]>();
User[] u = new User[3];
u[0] = new User();
u[0].setFirstName("Mike"); u[0].setLastName("Jordon");
u[1] = new User();
u[1].setFirstName("Jack"); u[1].setLastName("Nicolson");
u[2] = new User();
u[2].setFirstName("Jack"); u[2].setLastName("Nicolson");
a.add(u);
Set<User[]> s = new HashSet<User[]>(a);
for (User[] ss : s) {
for (int i=0; i<ss.length; i++) {
System.out.println(ss[i].getFirstName() + " " + ss[i].getLastName());
}
}
}
}
I'm expecting output to be
Mike Jordon
Jack Nicolson
But somehow, its retaining duplicate object & printing as:
Mike Jordon
Jack Nicolson
Jack Nicolson
Can any one tell me what I'm missing??
Thanks!
Your equals method should be like :
#Override
public boolean equals(Object obj) {
if(obj instanceof User) {
User temp = (User) obj;
if(this.firstName.equals(temp.firstName) && this.lastName.equals(temp.lastName)) {
return true;
}
}
return false;
}
I have gone through your questions and understood the requirement. Please find the similar kind of code I have implemented and also successfully removed objects from a collection those having duplicate values.
#Snipet...
Employee.java
==============
package com.hcl;
public class Employee {
public String empid;
public String empname;
public double sal;
public int age;
public Employee(){
}
public Employee(String empid,String empname,double sal,int age){
this.empid = empid;
this.empname = empname;
this.sal = sal;
this.age = age;
}
public String getEmpid() {
return empid;
}
public void setEmpid(String empid) {
this.empid = empid;
}
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* This override method playes a major role to remove duplicate values
*/
#Override
public int hashCode() {
return (this.empid.hashCode() + this.empname.hashCode()+String.valueOf(this.sal).hashCode()+String.valueOf(this.age).hashCode());
}
/**
* This override method plays a major role to remove duplicate values
*/
#Override
public boolean equals(Object obj) {
if(obj instanceof Employee) {
Employee temp = (Employee) obj;
if(this.empid.equals(temp.empid) && this.empname.equals(temp.empname) && String.valueOf(this.sal).equals(String.valueOf(temp.sal)) && String.valueOf(this.age).equals(String.valueOf(temp.age))) {
return true;
}
}
return false;
}
}
#Snipet..........
RemoveDuplicateObjects.java
=============================
package com.hcl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RemoveDuplicateObjects {
public static void main(String[] args) {
Employee emp1 = new Employee("1","bapi",1000,31);
Employee emp2 = new Employee("2","mano",2000,29);
Employee emp3 = new Employee("1","bapi",1000,31); // emp3 == emp1 duplicate object
Employee emp4 = new Employee("3","Rohan",3000,27);
Employee emp5 = new Employee("1","bapi",1000,31); // emp5 == emp3 == emp1 duplicate object
RemoveDuplicateObjects obj = new RemoveDuplicateObjects();
// empList contains objects having duplicate values. How to remove duplicate?
List<Employee> empList = new ArrayList<Employee>();
empList.add(emp1);
empList.add(emp2);
empList.add(emp3);
empList.add(emp4);
empList.add(emp5);
if(emp1.equals(emp2)){
System.out.println("emp1 and emp2 are equal");
}
if(emp1.equals(emp3)){
System.out.println("emp1 and emp3 are equal");
}
obj.removeDuplicate(empList);
}
// method is used for removing objects having duplicate values
private void removeDuplicate(List<Employee> empList) {
Set<Employee> empSet = new HashSet<Employee>();
empSet.addAll(empList);
for(Employee e: empSet){
System.out.println("id = "+e.getEmpid());
System.out.println("name = "+e.getEmpname());
System.out.println("sal = "+e.getSal());
System.out.println("age = "+e.getAge());
}
}
}
Done! Now you can run the program and analyze the solution.
I suppose you want this:
class pp {
public static void main(String[] args) {
Set<User> a = new HashSet<User>();
User u = new User();
u.setFirstName("Mike"); u.setLastName("Jordon");
a.add(u);
u = new User();
u.setFirstName("Jack"); u.setLastName("Nicolson");
a.add(u);
u = new User();
u.setFirstName("Jack"); u.setLastName("Nicolson");
a.add(u);
for (User ss : a) {
System.out.println(ss.getFirstName() + " " + ss.getLastName());
}
}
}
try this my friend :
Iterator i = a.iterator();
while (i.hasNext()) {
User u = (User) i.next();
boolean match = false;
Iterator j = a.iterator();
boolean once = true;
while (j.hasNext()) {
if(once){j.next();} // to skip own occurence only once
once = false;
User u2 = (User) j.next();
if (u.getFirstName().equals(u2.getFirstName())
&& u.getLastName().equals(u2.getLastName())) {
match = true;
}
}
if (!match) {
// print
}
}
Override the equals method as suggested by Jason.
Now for removing duplicates you need to use Set.
List allows duplicate values so you will always have duplicate values. Set doesnot allow duplicate value so it will solve your problem.
You're using a Set of arrays, where the set has one element, namely an array of three Users. Arrays don't enforce or check uniqueness, which is why you get the same User twice. If you removed the arrays altogether, and simply used a Set, you'd get the "unique" behaviour you want.
First you should use a Set to store objects instead of a array if you dont want duplicates. (Arrays and List do allow duplicate objects to be stores)
Second your equals methods should use String.equal method for comparison and should check for null values too to be on the safe side. I would use the IDE's auto generate feature for hashcode and equals methods always (i.e. Eclipse Source -> Generate hashCode() and equals()...)
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.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;
User other = (User) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
and main method
public static void main(String[] args) {
List<Set<User>> a = new ArrayList<Set<User>>();
Set<User> set = new HashSet<User>();
User u = new User();
u.setFirstName("Mike"); u.setLastName("Jordon");
set.add(u);
u = new User();
u.setFirstName("Jack"); u.setLastName("Nicolson");
set.add(u);
u = new User();
u.setFirstName("Jack"); u.setLastName("Nicolson");
set.add(u);
a.add(set);
for (Set<User> ss : a) {
for (User user : ss) {
System.out.println(user.getFirstName() + " " + user.getLastName());
}
}
}
In general, you can add elements to a Set to remove duplicates. However, you don't want to add the entire array to the collection in general; you just want to add the individual elements, like so:
public static void main(String[] args) {
Set<User> a = new HashSet<User>();
User[] u = new User[3];
u[0] = new User();
u[0].setFirstName("Mike"); u[0].setLastName("Jordon");
u[1] = new User();
u[1].setFirstName("Jack"); u[1].setLastName("Nicolson");
u[2] = new User();
u[2].setFirstName("Jack"); u[2].setLastName("Nicolson");
// Add each of the users to the Set. Note that there are three.
for (User user : u) {
a.add(u);
}
// Get the results back as an array. Note that this will have two.
User[] duplicatesRemoved = new User[0];
a.toArray(duplicatesRemoved);
}
Hi you can write a method in pp class in order to remove duplicate elements from the user array as follows :
private User[] getUserArrayWithoutDuplicates(User[] a) {
int count = a.length;
Set<User> tempset = new HashSet<User>();
for (int i = 0; i < count; i++) {
User[] user = a;
int arraysize = user.length;
for (int j = 0; j < arraysize; j++)
tempset.add(user[j]);
}
User[] usr = new User[tempset.size()];
Iterator<User> tempIterator = tempset.iterator();
int p = 0;
while (tempIterator.hasNext()) {
User user = tempIterator.next();
usr[p] = new User();
usr[p].setFirstName(user.firstName);
usr[p].setLastName(user.lastName);
p++;
}
return usr;
}
This method will remove the duplicate entries from User array and return the User array without duplicate entries.