best way to find extra object among two lists - java

I have two custom lists say CompanyList such that
public class CompanyList<E> extends Collection<E> implements List<E> {}
Here I have list of CompanyList such that
public class CompanyMakeVO extends BaseVO {
private static final long serialVersionUID = 1L;
private String name;
public CompanyMakeVO() {
super();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
// overrides equals
public boolean equals(Object obj) {
if (obj == null || !(obj.getClass() == this.getClass())) {
return false;
}
CompanyMakeVO make = (CompanyMakeVO) obj;
// NAME
String thisName = this.getName();
String thatName = make.getName();
if (null == thisName || null == thatName)
return false;
return thisName.equals(thatName);
}
// hashcode
public int hashCode() {
return getName().hashCode();
}
}
I have two such lists say oldList and newList both have some objects of CompanyMakeVO, each object represents a company name via name attribute.
Lets say Old list has 3 objects with name as Audi, BMW and Aston Martin while new list has 5 objects with name as Audi, BMW, Aston Martin, Jaquar and Tesla. The Lists will not have any duplicates items i.e comapny name will not be repeated. I need to find the unique element present in either list and also with the list name and element name.
What's the best way to find it out?

For small data sets, lists with a few elements, it is convenient to use List.removeAll().
For large data sets, like lists with millions of items, you can use a HashMap to get those elements.
Since List.removeAll() will try to compare each item in the first list against all elements in the second list, which is O(NM) complexity. For using HashMap, it only needs O(N+M), faster than the first one.

You can use removeAll() method from ArrayList as given below:
List<CompanyMakeVO> companyMakeVOListOld = new ArrayList<>();
//add your items to the old list
List<CompanyMakeVO> companyMakeVOListNew = new ArrayList<>();
//add your items to new list
//now removeAll duplicate items from new list by passing the old list
companyMakeVOListNew.removeAll(companyMakeVOListOld);
ArrayList - removeAll method API:
public boolean removeAll(Collection c)
Removes from this list all of its elements that are contained in the
specified collection.
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#removeAll(java.util.Collection)

Related

How to apply comparator to get the TOP 5 cities from a List sorted by population?

I have a class City
public final class City {
private final String name;
private final String state;
private final int population;
public City(String name, String state, int population) {
this.name = name;
this.state = state;
this.population = population;
}
public String getName() {
return name;
}
public String getState() {
return state;
}
public int getPopulation() {
return population;
}
#Override
public String toString() {
return "City [name=" + name + ", state=" + state + ", population=" + population + "]";
}
}
And a class that implements Observable (not needed for this). This observable class holds an arraylist List<City> cityList that has the data for all the cities that have been reported.
My class TopFiveCities is supposed to:
"implement a getter method getTopFive() returning a list with the five
top cities (in terms of population) received. The list is sorted from
higher to lower numbers. The returned list must be a copy of the list
kept by the observer"
Aside from just getting the top 5 list, I also need to know how to make a copy of that list from the observer.
This is what I have:
public class TopFiveCities
implements Observer {
// THIS ALSO DOESN'T WORK UNLESS THE LIST IS STATIC
// SO HOW CAN I MAKE A COPY OF THE LIST FROM OBSERVER?
private List<City> list = new ArrayList<>(CensusOffice.cityList);
public List<City> getTopFive() {
Collections.sort(list, new Comparator<City>() {
#Override
public int compare(City o1, City o2) {
return Integer.compare(o1.getPopulation(), o2.getPopulation());
}
});
return list;
}
public void update(Observable observable) {
if (!(observable instanceof Observable)) {
throw new IllegalArgumentException();
}
}
}
With this, when one of the sample outputs should be:
City [name=Chicago, state=IL, population=2746388]
I just receive a list of all the cities, sorted by population from LOWEST to HIGHEST. What I'm doing wrong?
You can just use a Stream, use a Comparator to sort the stream, limit the number of element and convert the elements to a new list:
List<City> top5citiesByPopulation = cities.stream()
.sorted(Comparator.comparing(City::getPopulation).reversed())
.limit(5)
.collect(Collectors.toList());
int order = requestedOrder.equals("asc") ? 1 : -1;
Collections.sort(list, new Comparator<CustomObj>() {
public int compare(CustomObj first, CustomObj scnd) {
return first.getComparableParam().compareTo(scnd.getComparableParam()) * order;
}
});
I just copied and passed this code block from recommended stackover page in the comment. Of you want ascending order simply change it. In your code order will be -1.
Simply you need to multiply by -1.
return Integer.compare(o1.getPopulation(), o2.getPopulation()) * -1;
After this you can sublist it.
You keep the list as global variable it can be reached from update method but it does not change if class is singleton except for update method. Your update method can change it by notifying
In update method you can simply clear and add new list by list.addAll
Since this is a schoolwork assignment, I’ll describe the pieces but let you assemble them into final code.
I have a class "City"
You could more briefly define that class as a record.
City ( String name, String state, int population ) {}
holds an array list "List cityList"
List < City > cities = new ArrayList<>();
getting the top 5 list
Sort the list by using a reverse comparator. You can make a comparator for sorting by using a method reference for the accessor “getter” method. But be aware that records do not use the word “get” by default as their accessor, they use simply the name of the property.
cities.sort( Comparator.comparing( City :: population ).reversed() ) ;
For an unmodifiable list, call List.of or List.copyOf.
List#subset gives you a list with some of the elements of the original. But beware: the resulting list is based on a view of the original list. The subset is not separate and independent. To get a separate list, pass to List.copyOf or pass to the constructor of another List implementation.
List< City > topFivePop = List.copyOf( subset ) ;
This problem doesn't require sorting the whole given list, which will have a time complexity of O(n log n).
Basically, the task is somewhat similar to finding the maximum element in the list that can be done in O(n) time with a single pass through the given list.
The most suitable approach for this problem is a partial sorting, and the best performance that could be achieved is the middle-ground between O(n) and O(n log n).
In order to find 5 maximum elements in a list, we can maintain a collection that will store in sorted order up to 5 previously encountered elements with maximum values.
Elements that are lover than the smallest element in the collection will be discarded automatically if the collection is already of size 5. Only new elements with a value higher than the smallest element's value will trigger reordering of this tiny collection. I.e. the data will be sorted partially instead of sorting the whole data set.
In the implementation below for as collection that will store 5 max elements, I've chosen a PriorityQueue.
According to the documentation it's methods have the following time complexity.
this implementation provides O(log(n)) time for the enqueuing and dequeuing methods (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; and constant time for the retrieval methods (peek, element, and size).
I.e. adding of a new element and removing the first element both will perform in logarithmic time, and accessing the smallest value in the queue with the method element() with done in O(1) time (almost immediately).
The PriorityQueue is encapsulated inside a generic class, which constructor expects as parameters a Comparator<T> and a desired maximum size of the queue (i.e. a number of max elements that needs to be found).
The queue itself doesn't exposed to the outside classes. Method addItem() processing the given element and getFirstN returns a sorted immutable list.
Comparator in the code below is implemented using the static method comparingInt(). You could also implement Comparator in a "classical way" (pre-Java 8) by providing the behavior for it's abstract method compare() either by using a lambda expression or within an anonymous inner class.
public class FirstN<T> {
private final Queue<T> queue;
private final Comparator<T> comparator;
private final int capacity;
public FirstN(Comparator<T> comparator, int capacity) {
this.queue = new PriorityQueue<>(comparator);
this.comparator = comparator;
this.capacity = capacity;
}
public boolean addItem(T item) {
if (capacity == queue.size() && comparator.compare(item, queue.element()) <= 0) {
return false; // queue is full and the given item is smaller than the lowerest element in the queue
}
if (capacity == queue.size() && comparator.compare(item, queue.element()) > 0) {
queue.remove(); // removing the first element if it's smaller than the given item
}
return queue.add(item); // adding the given item
}
public List<T> getFirstN() {
List<T> result = new ArrayList<>(queue); // creating a list based on a queue
result.sort(comparator);
return List.copyOf(result); // making a copy of the list (returned list is immutable)
}
}
main()
public static void main(String[] args) {
List<City> cities = List.of(
new City("Austin", "Texas", 1028225),
new City("Los Angeles", "California", 3985516),
new City("San Diego", "California", 1429653),
new City("Houston", "Texas", 2325353),
new City("Phoenix", "Arizona", 1759943),
new City("New York City", "New York", 8177025),
new City("San Antonio", "Texas", 1598964),
new City("Philadelphia", "Pennsylvania", 1585480),
new City("San Diego", "California", 1429653),
new City("Chicago", "Illinois", 2671635),
new City("Dallas", "Texas", 1348886));
FirstN<City> top5Cities =
new FirstN<>(Comparator.comparingInt(City::getPopulation), 5);
for (City next: cities) {
top5Cities.addItem(next);
}
List<City> result = top5Cities.getFirstN(); // contains 5 biggest US cities
result.forEach(System.out::println); // printing the result
}
Output (order from lowest to highest)
City [name=Phoenix, state=Arizona, population=1759943]
City [name=Houston, state=Texas, population=2325353]
City [name=Chicago, state=Illinois, population=2671635]
City [name=Los Angeles, state=California, population=3985516]
City [name=New York City, state=New York, population=8177025]

The most efficient method to find an element in Set

I have the Person class:
public class Person implements Comparable<Person> {
private int id;
#Override
public int hashCode() {
return id;
}
#Override
public boolean equals(Object obj) {
Person other = (Person) obj;
return id == other.id;
}
#Override
public int compareTo(Person o) {
return Integer.compare(id, o.id);
}
}
And I have TreeSet of persons.
I need to implement method findPersonById(int id) in TreeSet.
I made it this way:
public Person find(int id) {
List<Person> personList = new ArrayList(idTreeSet);
Person pattern = new Person(id);
int index = Collections.binarySearch(personList, pattern);
return index < 0 ? null : personList.get(index);
}
Now the efficient of the find method is O(n) because it needs to copy all of elements from TreeSet to ArrayList.
But is there more efficient way to implement this method?
I don't need a Map. I'm interesed to resolve it without Maps.
Since you are prepared to allocate a temporary Person object, you can do it like this:
public Person find(int id) {
Person temp = new Person(id);
Person candidate = idTreeSet.ceiling(temp);
return temp.equals(candidate) ? candidate : null;
}
This is O(logN).
Note that we only create one temporary object here. If we use tailSet or subSet we will be creating at least second one; i.e. the NavigableSet returned by the tailSet or subSet call. (Looking under the hood of the TreeSet implementation, it looks like more will be created.)
If you don't need the properties of a TreeSet then using a HashMap<Integer, Person> or a HashSet<Person> would give you O(1) lookup. But in the latter case, you need change your Person class to satisfy the equals / hashCode contract.
Because TreeSet is a NavigableSet, you can use TreeSet.subSet, which leverages knowledge about the order of the elements to extract a range of elements as close as possible to the element you are interested in:
Person pattern = new Person(id);
return
// Get the Persons between pattern (inclusive) and pattern (inclusive).
// In other words: all the Persons with id equal to the input,
// of which there are zero or one.
idTreeSet.subSet(pattern, true, pattern, true).stream()
.findFirst()
.orElse(null);
Map<Integer, Person> personsById = new HashMap<>();
Would definitely be fastest, though not Tree based. A LinkedHashMap for order of insert would allow some order.
It is the more rugged solution.

Sort List of Objects Based On Another Sorted List Object Positions In Java

I have two list objects like :
public class AttributeMaster {
public String attribute_id;
public String view_index;
...
}
List<AttributeMaster> attributes = new ArrayList<AttributeMaster>();
public class AttributeDetail {
public String attribute_id;
public String attribute_name;
...
}
List<AttributeDetail> attribute_detail = new ArrayList<AttributeDetail>();
Here, I need to sort attribute_detail list based on list attributes. List attribute is already sorted based on its view_index property.
I want to update second list based on index of attribute_master list.
If one one can help.
Collections.sort(attribute_detail,
Comparator.comparing(item -> attributes.indexOf(item)));
int start_index=0;
for(int i=0;i<attributes.size();i++) {
for(int j=0;j<attribute_detail.size();j++) {
if(attributes.get(i).getAttribute_id().equals(attribute_detail.get(j).getAttribute_id())){
AttributeDetail temp=attribute_detail.get(start_index);
attribute_detail.set(start_index, attribute_detail.get(j));
attribute_detail.set(j,temp);
start_index++;
}
}
}
Through index iterations, the code will check the object existence for both list using attribute_id. If the object present in both list, then it will sort the list of attribute_detail based on the index of attributes list.

How to give Rank in list of Object and sort on the basis of Rank and any other key?

I have a situation where I need to make a list of object , where Object have (Name, Title) element. for e.g. :
obj1 = ('john', 'colonel')
obj2 = ('Alex', 'major')
obj3 = ('Roy', 'Major general')
obj4 = ('derrick', 'no Rank')
I need to do sorting in two ways:
1. First on the Title basis.
2. For any two names, if the title is same then sorting of object on the
alphabetical name basis(like chronological order of name).
3. And also need to remove duplicate names from the list.
Please help me out as I know how to sort the arraylist but don't know how to give ranking and sort on multiple conditions. If you need further details or do not understand my question then please let me know.
You need to create a Comparator Class, something like this:
class ObjectComparator implements Comparator<SomeClass>{
#Override
public int compare(SomeClass obj1, SomeClass obj2) {
// if two objects' ranks are the same
if(obj1.rank.equals(obj2.rank)){
// then compare their names
return obj1.name.compareTo(obj2.name);
}
else{
return obj1.rank.compareTo(obj2.rank);
}
}
}
I cannot understand how you want to remove the duplicates of the attribute name because that will ruin the sorting, unless you mean to remove the duplicates of the same name and rank (e.g. obj1 = "john" "colonel" and also obj2 ="john" "colonel") then in this case straightforwardly use a Set after overriding the equal and hashCode methods in your class, so your set can know if the two objects are equal...something like this:
#Override
#Override
public boolean equals(Object other){
if(other==null) return false;
if (other == this) return true;
return ((SomeClass)other).name.equalsIgnoreCase(this.name)
&& ((SomeClass)other).rank.equalsIgnoreCase(this.rank);
}
#Override
public int hashCode() {
return Objects.hash(name, rank);
}
But if you mean that you want to remove the duplicates names (i.e. objects have same fields but different ranks), then you may consider using a LinkedHashMap which preserves the order of items, but as I mentioned, this will Not preserve the sorting order for the particular duplicate (TBH I don't think that what you meant by duplicates)... an example for testing:
public class SomeClass{
String name, rank;
public SomeClass(String name, String rank){
this.name = name;
this.rank = rank;
}
public static void main(String[] args) {
SomeClass obj1 = new SomeClass("john", "colonel");
SomeClass obj2 = new SomeClass("XXX", "major");
SomeClass obj3 = new SomeClass("Roy", "general");
SomeClass obj4 = new SomeClass("derrick", "no Rank");
SomeClass obj5 = new SomeClass("john", "something");
SomeClass obj6 = new SomeClass("Alex", "major");
List<SomeClass> list = new ArrayList<>();
list.add(obj1); list.add(obj2); list.add(obj3);
list.add(obj4); list.add(obj5); list.add(obj6);
System.out.println(getSortedObjects(list));
}
public static Map<String, List<String>> getSortedObjects(List<SomeClass> list){
Map<String, List<String>> sortedMap = new LinkedHashMap<>();
// sort the list
Collections.sort(list, new ObjectComparator());
for(SomeClass obj : list){
// for testing sorting before ruining it in a map
System.out.println(obj.name + " " + obj.rank);
List<String> temp;
if(sortedMap.containsKey(obj.name)){
temp = sortedMap.get(obj.name);
temp.add(obj.rank);
sortedMap.put(obj.name, temp);
}
else{
temp = new ArrayList<>();
temp.add(obj.rank);
sortedMap.put(obj.name, temp);
}
}
return sortedMap;
}
}
Test
john colonel
Roy general
Alex major
XXX major
derrick no Rank
john something
{john=[colonel, something], Roy=[general], Alex=[major], XXX=[major], derrick=[no Rank]}
Complete Implementation Example of Set & Comparator:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
public class SomeClass{
// suppose the fields in your class
String name, rank;
// simple constructor
public SomeClass(String name, String rank){
this.name = name;
this.rank = rank;
}
// you need to override both equals and hashCode methods from
// Object superclass in order the set works properly and realizes
// that two given objects of type SomeClass are equal
#Override
public boolean equals(Object other){
if(other==null) return false;
if (other == this) return true;
return ((SomeClass)other).name.equalsIgnoreCase(this.name)
&& ((SomeClass)other).rank.equalsIgnoreCase(this.rank);
}
#Override
public int hashCode() {
return Objects.hash(name, rank);
}
#Override
public String toString(){
return name + " " + rank;
}
/**
* Pass a set then add the items to a temporary ArrayList
* to be sorted according to the Comparator condition
* then clear the set and add the sorted element back
* #param list
*/
public static void sort(Set<SomeClass> list){
List<SomeClass> temp = new ArrayList<>();
temp.addAll(list);
// anonymous class implementation of Comparator
Collections.sort(temp, new Comparator<SomeClass>(){
#Override
public int compare(SomeClass obj1, SomeClass obj2) {
// if two objects' ranks are the same
if(obj1.rank.equals(obj2.rank)){
// then compare their names
return obj1.name.compareTo(obj2.name);
}
else{
return obj1.rank.compareTo(obj2.rank);
}
}});
list.clear();
list.addAll(temp);
}
// testing
public static void main(String[] args) {
// create Objects of the class with duplicate
SomeClass obj1 = new SomeClass("john", "colonel");
SomeClass obj2 = new SomeClass("XXX", "major");
SomeClass obj3 = new SomeClass("Roy", "general");
SomeClass obj4 = new SomeClass("derrick", "no Rank");
SomeClass obj5 = new SomeClass("john", "something");
SomeClass obj6 = new SomeClass("Alex", "major");
SomeClass obj7 = new SomeClass("Alex", "major"); // duplicate object
// LinkedHashSet PRESERVES the order of elements' insertion
// in addition to removing duplicates
Set<SomeClass> list = new LinkedHashSet<>();
// populate the set
list.add(obj1); list.add(obj2); list.add(obj3);
list.add(obj4); list.add(obj5); list.add(obj6);
list.add(obj7);
//before sorting
System.out.println("Before " + list);
// after sorting
sort(list);
System.out.println("After " + list);
}
}
Test:
Before [john colonel, XXX major, Roy general, derrick no Rank, john something, Alex major]
After [john colonel, Roy general, Alex major, XXX major, derrick no Rank, john something]
You need to read up on Comparators, and implement one for your class.
If you are using Java 8, the easiest way is to use Comparators.comparing
https://praveer09.github.io/technology/2016/06/21/writing-comparators-the-java8-way/
In order to prevent duplicate names, I would keep a separate Set (which automatically constrains uniqueness) of names that already exist. Check to see if that set contains the names before you add the new object to the arraylist.

Java, searching within a list of objects?

I'm a bit lost on the way to make this happen the fastest. I have a large list of objects that have basic variable attributes (with getters / setters) and I need to do a search in this list to find the objects within the list that match a given parameter
I have found how to do a regular list search but I need to, for example search for the value of the result of doing a call getName() for each object in the list and get objects that have a result that matches my input.
Something like below where the third argument is the result of the method call and the second is what I am trying to find.
int index = Collections.binarySearch(myList, "value", getName());
Any advice is appreciated
If you just as a one-off operation need to find the object(s) whose getName() is a particular value, then there's probably not much magic possible: cycle through the list, call getName() on each object, and for those that match, add them to your list of results.
If getName() is an expensive operation and there's some other way of a-priori working out if a given object definitely won't return a matching value, then obviously you can build in this 'filtering' as you cycle through.
If you frequently need to fetch objects for a given getName(), then keep an index (e.g. in a HashMap) of [result of getName()->object -> list of matches]. You'll need to decide how and if you need to keep this "index" in synch with the actual list.
See also the other proposition to use binarySearch() but to keep the list maintained. This way, inserts are more expensive than with a map and unsorted list, but if inserts are infrequent compared to lookups, then it has the advantage of only needing to maintain one structure.
Take a look at the binarySearch that takes a comparator:
public static int binarySearch(List list,
T key,
Comparator c)
So you would do something like:
class FooComparator
implements Comparator<Foo>
{
public int compare(T a, T b)
{
return (a.getName().compareTo(b.getName());
}
}
int index = Collections.binarySearch(myList, "value", new FooComparator());
You will need to first sort the list of course (Collections.sort takes a Comaprator as well...).
I know anonymous inner classes are not fashion anymore, but while Java 8 arrives, you can create something like this:
1.- Create a search method that iterates the collection and pass an object that tells you if your object is to be returned or not.
2.- Invoke that method and create an anonymous inner class with the criteria
3.- Get the new list in separate variable.
Something like this:
result = search( aList, new Matcher(){ public boolean matches( Some some ) {
if( some.name().equals("a")) {
return true;
}
}});
Here's a working demo:
import java.util.*;
class LinearSearchDemo {
public static void main( String ... args ) {
List<Person> list = Arrays.asList(
Person.create("Oscar", 0x20),
Person.create("Reyes", 0x30),
Person.create("Java", 0x10)
);
List<Person> result = searchIn( list,
new Matcher<Person>() {
public boolean matches( Person p ) {
return p.getName().equals("Java");
}});
System.out.println( result );
result = searchIn( list,
new Matcher<Person>() {
public boolean matches( Person p ) {
return p.getAge() > 16;
}});
System.out.println( result );
}
public static <T> List<T> searchIn( List<T> list , Matcher<T> m ) {
List<T> r = new ArrayList<T>();
for( T t : list ) {
if( m.matches( t ) ) {
r.add( t );
}
}
return r;
}
}
class Person {
String name;
int age;
String getName(){
return name;
}
int getAge() {
return age;
}
static Person create( String name, int age ) {
Person p = new Person();
p.name = name;
p.age = age;
return p;
}
public String toString() {
return String.format("Person(%s,%s)", name, age );
}
}
interface Matcher<T> {
public boolean matches( T t );
}
Output:
[Person(Java,16)]
[Person(Oscar,32), Person(Reyes,48)]
To do this in a more scalable way, without simply iterating/filtering objects, see this answer to a similar question: How do you query object collections in Java (Criteria/SQL-like)?
If the objects are immutable (or you at least know their names won't change) you could create an index using a HashMap.
You would have to fill the Map and keep it updated.
Map map = new HashMap();
map.put(myObject.getName(), myObject);
... repeat for each object ...
Then you can use map.get("Some name"); to do lookup using your index.
One library I'm familiar with is Guava -- you can compose its Predicate to pull out items from an Iterable. There's no need for the collection to be pre-sorted. (This means, in turn, that it's O(N), but it's convenient.)

Categories

Resources