The most efficient method to find an element in Set - java

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.

Related

Java 9 processing list of objects with nullable fields into another list

I have a list of objects like
#Getter
#Setter
public class Person {
private String name;
private boolean value_bool;
private String value_string;
private Integer value_integer;
private String value_text;
}
The problem is, only ONE of these fields (except for name) is actually initiated (e.g. non-null) for one Person, every other one is null, so for 4 of these values there are 4 Person instances each having a name and one of the values. How do I transform this list of Person objects into a list of value objects, preferably using streamAPI?
The boolean value_bool cannot be null.
To merge this data, first you'd want to group by name, and then collapse a List<Person> (which would represent the separate Person objects all with the same name) into a single merged Person.
To group:
Map<String, List<Person>> grouped = persons.stream()
.collect(Collectors.groupingBy(Person::getName));
Merging a List<Person> into a single Person is not particularly suitable to stream-based code. I would just make a method, it's a little complicated as you have lots of non-obvious choices to make. For example, let's say you have 2 with value_bool is false, and 1 with true. And 2 Person objects both have a non-null value_string, and they aren't the same string; which one wins, or should an exception be thrown, or should the strings be concatenated?
The fact that such questions exist, and have no obvious answers, should strongly indicate that there is no simple one-liner that could possibly do this.
Thus, something like:
public class Person {
// ....
public static Person merge(#NonNull Collection<? extends Person> persons) {
if (persons.isEmpty()) throw new IllegalArgumentException(
"Cannot merge an empty list of persons");
if (persons.size() == 1) return persons.iterator().next();
Person out = null;
for (Person person : persons) {
if (out == null) {
out = person;
continue;
}
// If any part-person is 'valueBool', the output is also.
if (person.isValueBool()) out.setValueBool(true);
// For the rest, the 'last' non-null value 'wins':
if (person.getValueString() != null) out.setValueString(person.getValueString());
// etc
}
return out;
}
}
armed with that method:
persons.stream()
.collect(Collectors.groupingBy(Person::getName))
.values()
.stream()
.map(Person::merge)
.collect(Collectors.toList());
Where that last line can be toList(); if you have JDK16.

How can I sort my arraylist named "serviceNeedingTasks" based on two attributes of an object [duplicate]

I want to sort a List of objects by a specified attribute of those objects and I want to choose which attribute should be used for sorting. Example:
class Car{
private String name;
private String colour;
public enum sortBy {NAME, COLOUR};
public String name(){
return name;
}
public String colour(){
return colour;
}
public static Car[] getSortedArray(Car[] carArray, sortBy sortType){
HashMap<Object, Car> carMap = new HashMap<Object, Car>();
Object[] sortArray = new Object[carArray.length];
Object value = null;
for(int i = 0; i < carArray.length; i++){
if(sortType == sortBy.NAME){
value = carArray[i].name();
}else if(sortType == sortBy.COLOUR){
value = carArray[i].colour();
}
carMap.put(value, carArray[i]);
sortArray[i] = value;
}
Arrays.sort(sortArray);
Car[] sortedArray = new Car[sortArray.length];
for(int i = 0; i < sortArray.length; i++){
sortedArray[i] = carMap.get(sortArray[i]);
}
return sortedArray;
}
}
//external:
Car[] cars = getSomeCars();
Car[] nameSortedCars = Car.getSortedArray(cars, Car.sortBy.NAME);
Car[] colourSortedCars = Car.getSortedArray(cars, Car.sortBy.COLOUR);
The idea is simple:
I put all values that i want to sort by into an array, and i create a map that maps these values back to their objects. After I sorted this array I take the objects mapped to these values and put them in the same order into a new array which is then sorted by these values. The values are just created with type Object so I can sort by multiple types (not just Strings as in the example).
This works fine unless you have two objects with the same attribute value, then only one object will be in the returned array, but two times.
Is there a better way to achieve this sorting?
It would be much simpler to use custom comparators:
To sort by name:
Arrays.sort(carArray, Comparator.comparing(Car::name));
To sort by colour:
Arrays.sort(carArray, Comparator.comparing(Car::colour));
So you could modify getSortedArray():
public static Car[] getSortedArray(Car[] carArray, Comparator<Car> comparator) {
Car[] sorted = carArray.clone()
Arrays.sort(sorted, comparator);
return sorted;
}
And call it like this:
Car[] sorted = getSortedArray(carArray, Comparator.comparing(Car::name));
Edit:
If you use a language version that does not support these features, you can create the comparators by explicitly creating a nested class that implements the Comparator interface.
This, for example, is a singleton Comparator that compares Car instances by name:
static enum ByName implements Comparator<Car> {
INSTANCE;
#Override
public int compare(Car c1, Car c2) {
return c1.name().compareTo(c2.name());
}
}
Then call:
Car[] sorted = getSortedArray(carArray, ByName.INSTANCE);
TL;DR: There's already a wheel for that.
I would say the easiest way to do this is to create a comparator:
final Comparator<Car> byName = Comparator.comparing(Car::name);
final Comparator<Car> byColour = Comparator.comparing(Car::colour);
Then just use the appropriate method on Arrays to sort by a comparator:
Arrays.sort(carArray, byName);
Now you want to do it with an enum? Just have the enum implements Comparator<Car>:
enum SortBy implements Comparator<Car> {
NAME(Comparator.comparing(Car::name)),
COLOUR(Comparator.comparing(Car::colour));
private final Comparator<Car> delegate;
private SortBy(Comparator<Car> delegate) {
this.delegate = delegate;
}
#Override
public int compare(final Car o1, final Car o2) {
return delegate.compare(o1, o2);
}
}
Want to sort by name then by colour? Easy:
final Comparator<Car> byName = SortBy.NAME.thenComparing(SortBy.COLOUR);
Want to sort by name in reverse order? Easy:
final Comparator<Car> byName = SortBy.NAME.reversed();
You're reinventing the wheel! Life will be much easier for you if you use the templated Collections API. To do this, you would work with List instead of arrays, define a Comparator to do your sorting, and then let the API do the work for you.
Comparator<Car> carComparator = new Comparator<Car>(){
public int sort(Car car1, Car car2){
//Sorting logic goes here.
}
}
List<Car> cars = getCars();
cars = Collections.sort(cars, carComparator); //the cars collection is now sorted.
If you wanted to sometimes sort by one attribute or another, you could make my variable carComparator into its own class and define which attributes to sort by in the constructor.
Hope that helps :)
Edit: As others have pointed out, this approach also works with arrays. But unless you have a good reason to be working with Arrays, working with Collections will generally be easier.
I think the solution would be more efficient if you passed a Comparator implementation to the Arrays.sort. Right now, you are looping n*2 from the looks of it, the hash map (O(1)) plus the Arrays.sort (which is another 0(n log n) or such). If you do the below, you could skip the 2 loops, and the map, you are using currently.
You can simply create a Comparator like (rough code):
class CarComparator implements Comparator<Car> {
enum compareType; //plus setter
public int compareTo(Car a, Car b) {
if(compareType == COLOUR) return a.colour.compareTo(b.colour);
if(compareType == NAME.....
}
}
, and then simply send the array of Cars to
Arrays.sort(cars, new CarComparator(COLOUR))
, or use more specialised comparator classes, one for each attribute, and a factory to render them, and of course don't create a new Comparator() for each sort if this is happening often. :-)
Overall, this approach should make your code more efficient.
}

best way to find extra object among two lists

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)

Compare two sets of different types

I'm looking for a way to tell if two sets of different element types are identical if I can state one-to-one relation between those element types. Is there a standard way for doing this in java or maybe guava or apache commons?
Here is my own implementation of this task. For example, I have two element classes which I know how to compare. For simplicity, I compare them by id field:
class ValueObject {
public int id;
public ValueObject(int id) { this.id=id; }
public static ValueObject of(int id) { return new ValueObject(id); }
}
class DTO {
public int id;
public DTO(int id) { this.id=id; }
public static DTO of(int id) { return new DTO(id); }
}
Then I define an interface which does the comparison
interface TwoTypesComparator<L,R> {
boolean areIdentical(L left, R right);
}
And the actual method for comparing sets looks like this
public static <L,R> boolean areIdentical(Set<L> left, Set<R> right, TwoTypesComparator<L,R> comparator) {
if (left.size() != right.size()) return false;
boolean found;
for (L l : left) {
found = false;
for (R r : right) {
if (comparator.areIdentical(l, r)) {
found = true; break;
}
}
if (!found) return false;
}
return true;
}
Example of a client code
HashSet<ValueObject> valueObjects = new HashSet<ValueObject>();
valueObjects.add(ValueObject.of(1));
valueObjects.add(ValueObject.of(2));
valueObjects.add(ValueObject.of(3));
HashSet<DTO> dtos = new HashSet<DTO>();
dtos.add(DTO.of(1));
dtos.add(DTO.of(2));
dtos.add(DTO.of(34));
System.out.println(areIdentical(valueObjects, dtos, new TwoTypesComparator<ValueObject, DTO>() {
#Override
public boolean areIdentical(ValueObject left, DTO right) {
return left.id == right.id;
}
}));
I'm looking for the standard solution to to this task. Or any suggestions how to improve this code are welcome.
This is what I would do in your case. You have sets. Sets are hard to compare, but on top of that, you want to compare on their id.
I see only one proper solution where you have to normalize the wanted values (extract their id) then sort those ids, then compare them in order, because if you don't sort and compare you can possibly skip pass over duplicates and/or values.
Think about the fact that Java 8 allows you to play lazy with streams. So don't rush over and think that extracting, then sorting then copying is long. Lazyness allows it to be rather fast compared to iterative solutions.
HashSet<ValueObject> valueObjects = new HashSet<>();
valueObjects.add(ValueObject.of(1));
valueObjects.add(ValueObject.of(2));
valueObjects.add(ValueObject.of(3));
HashSet<DTO> dtos = new HashSet<>();
dtos.add(DTO.of(1));
dtos.add(DTO.of(2));
dtos.add(DTO.of(34));
boolean areIdentical = Arrays.equals(
valueObjects.stream()
.mapToInt((v) -> v.id)
.sorted()
.toArray(),
dtos.stream()
.mapToInt((d) -> d.id)
.sorted()
.toArray()
);
You want to generalize the solution? No problem.
public static <T extends Comparable<?>> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor) {
return Arrays.equals(
vos.stream()
.map(voKeyExtractor)
.sorted()
.toArray(),
dtos.stream()
.map(dtoKeyExtractor)
.sorted()
.toArray()
);
}
And for a T that is not comparable:
public static <T> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor, Comparator<T> comparator) {
return Arrays.equals(
vos.stream()
.map(voKeyExtractor)
.sorted(comparator)
.toArray(),
dtos.stream()
.map(dtoKeyExtractor)
.sorted(comparator)
.toArray()
);
}
You mention Guava and if you don't have Java 8, you can do the following, using the same algorithm:
List<Integer> voIds = FluentIterables.from(valueObjects)
.transform(valueObjectIdGetter())
.toSortedList(intComparator());
List<Integer> dtoIds = FluentIterables.from(dtos)
.transform(dtoIdGetter())
.toSortedList(intComparator());
return voIds.equals(dtoIds);
Another solution would be to use List instead of Set (if you are allowed to do so). List has a method called get(int index) that retrieves the element at the specified index and you can compare them one by one when both your lists have the same size. More on lists: http://docs.oracle.com/javase/7/docs/api/java/util/List.html
Also, avoid using public variables in your classes. A good practice is to make your variables private and use getter and setter methods.
Instantiate lists and add values
List<ValueObject> list = new ArrayList<>();
List<DTO> list2 = new ArrayList<>();
list.add(ValueObject.of(1));
list.add(ValueObject.of(2));
list.add(ValueObject.of(3));
list2.add(DTO.of(1));
list2.add(DTO.of(2));
list2.add(DTO.of(34));
Method that compares lists
public boolean compareLists(List<ValueObject> list, List<DTO> list2) {
if(list.size() != list2.size()) {
return false;
}
for(int i = 0; i < list.size(); i++) {
if(list.get(i).id == list2.get(i).id) {
continue;
} else {
return false;
}
}
return true;
}
Your current method is incorrect or at least inconsistent for general sets.
Imagine the following:
L contains the Pairs (1,1), (1,2), (2,1).
R contains the Pairs (1,1), (2,1), (2,2).
Now if your id is the first value your compare would return true but are those sets really equal? The problem is that you have no guarantee that there is at most one Element with the same id in the set because you don't know how L and R implement equals so my advise would be to not compare sets of different types.
If you really need to compare two Sets the way you described I would go for copying all Elements from L to a List and then go through R and every time you find the Element in L remove it from the List. Just make sure you use LinkedList instead of ArrayList .
You could override equals and hashcode on the dto/value object and then do : leftSet.containsAll(rightSet) && leftSet.size().equals(rightSet.size())
If you can't alter the element classes, make a decorator and have the sets be of the decorator type.

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