The goal here is to insert elements into a TreeSet where the ordering is done by a custom objects internal List.
public class CustomObject implements Comparable<CustomObject> {
List<Integer> innerObjectList = new ArrayList<>();
#Override
public boolean compareTo(CustomObject o) {
#compare natural ordering of elements in array.
}
}
This is what I'm currently doing, it works but I was hoping to find a native way to do it.
#Override
public int compareTo(#NotNull AisleMap o) {
if (o.aisles.size() > aisles.size())
return 1;
if (o.aisles.size() < aisles.size())
return -1;
for (int i = 0; i<aisles.size(); i++) {
int compare = o.aisles.get(i).compareTo(aisles.get(i));
if( compare != 0) {
return compare;
}
}
return 0;
}
I'm trying to figure out how to do something like o.innerObjectList.compare(toInnerObjectList); However I'm not entirely sure how to do this so that the TreeSet orders the objects by the array properly. Is there an easy way to compare the "natural" order of elements in an array to each other? Only way I thought of was to check for size then if equal check contents until one index of one array is larger than the same index of another.
ex: the ordering would be like so {1,2,5} -> {1,2,4} -> {1,2,3}
Related
So I'm trying to sort this Arraylist of "Movies" type, alphabetically based on the actor's name and containing no duplicates, which means using a treeSet.
So I have implemented the "comparable" interface and then didn't know what to do after for making the list sorted by alphabets
public int compareTo(Movie m){
for(Movie movileTitle: moviesList){
return movileTitle.getActor().compareTo(m.getActor());
}
return 0;
}
//this is where I'm trying add the arrayList of the movies and sort them using a treeSet
public TreeSet<Movie> movieListByOrder(){
TreeSet<Movie> movieTemp = new TreeSet<>();
//Note, "allMoviesList" is the the list that contains all movies(duplicates) that I'm trying
to add to the treeSet, so that it gets rid of duplicates and that is also ordered.
movieTemp.addAll(allMoviesList);
System.out.println(movieTemp);
return movieTemp;
}
Continuing in the direction in which you are already headed, you may try using an inline comparator when creating your TreeSet:
Set<Movie> treeSet = new TreeSet<Movie>(new Comparator<Movie>() {
#Override
public int compare(Movie m1, Movie m2) {
if (m1 == null) {
if (m2 != null) return -1;
else return 0;
}
else if (m2 == null) {
return 1;
}
else {
return m1.getActor().compareTo(m2.getActor());
}
}
});
A TreeSet is a sorted collection, meaning it will maintain the sort order imposed by the above comparator. So if you want to print out the contents of the set in order, you need only iterate through it.
All that looks archaic, sorry.
Try to look into this code and decide if it's more interesting.
movies.stream()
.sorted(comparing(Movie::getActor))
.distinct()
.collect(toList());
PS: play with performance tuning for extra credit
Suppose I have the following List, which contains items of type ENTITY. ENTITY has an integer field which determines its natural ordering. I want to get the ENTITY which is the maximum or minimum based on that field's value. How can I implement this in Java?
List<ENTITY> lt = new ArrayList<ENTITY>();
class ENTITY
{
int field;
/* Constructor, getters, setters... */
}
Use Collections.sort with a Comparator to sort your list. Depending on whether you sort ascending or descending, the positions of the max and min elements will differ. In either case, they will be at opposite ends, one at the top of the list and one at the bottom. To sort in ascending order (smallest element at the beginning of the list, largest at the end) you can use something like this:
Collections.sort(lt, new Comparator<ENTITY> {
public int compare(ENTITY o1, ENTITY o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
}
return -1;
}
else if (o2 == null) {
return 1;
}
// If field is Comparable:
return o1.getField().compareTo(o2.getField());
// OR - If field is an int
return o1.getField() < o2.getField() ? -1 : (o1.getField() > o2.getField() ? 1 : 0);
}
});
//stream the elements, map to their fields, and get the max
return lt.stream().max((e1, e2) -> Integer.compare(e1.filed, e2.filed)).orElse(/* default */);
Just one of the many applications of Java 8's stream api.
Though I would suggest working on some coding conventions first.
I need a Collection that sorts the element, but does not removes the duplicates.
I have gone for a TreeSet, since TreeSet actually adds the values to a backed TreeMap:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
And the TreeMap removes the duplicates using the Comparators compare logic
I have written a Comparator that returns 1 instead of 0 in case of equal elements. Hence in the case of equal elements the TreeSet with this Comparator will not overwrite the duplicate and will just sort it.
I have tested it for simple String objects, but I need a Set of Custom objects.
public static void main(String[] args)
{
List<String> strList = Arrays.asList( new String[]{"d","b","c","z","s","b","d","a"} );
Set<String> strSet = new TreeSet<String>(new StringComparator());
strSet.addAll(strList);
System.out.println(strSet);
}
class StringComparator implements Comparator<String>
{
#Override
public int compare(String s1, String s2)
{
if(s1.compareTo(s2) == 0){
return 1;
}
else{
return s1.compareTo(s2);
}
}
}
Is this approach fine or is there a better way to achieve this?
EDIT
Actually I am having a ArrayList of the following class:
class Fund
{
String fundCode;
BigDecimal fundValue;
.....
public boolean equals(Object obj) {
// uses fundCode for equality
}
}
I need all the fundCode with highest fundValue
You can use a PriorityQueue.
PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>();
PriorityQueue(): Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.
This is a link to doc: https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html
I need all the fundCode with highest fundValue
If that's the only reason why you want to sort I would recommend not to sort at all. Sorting comes mostly with a complexity of O(n log(n)). Finding the maximum has only a complexity of O(n) and is implemented in a simple iteration over your list:
List<Fund> maxFunds = new ArrayList<Fund>();
int max = 0;
for (Fund fund : funds) {
if (fund.getFundValue() > max) {
maxFunds.clear();
max = fund.getFundValue();
}
if (fund.getFundValue() == max) {
maxFunds.add(fund);
}
}
You can avoid that code by using a third level library like Guava. See: How to get max() element from List in Guava
you can sort a List using Collections.sort.
given your Fund:
List<Fund> sortMe = new ArrayList(...);
Collections.sort(sortMe, new Comparator<Fund>() {
#Override
public int compare(Fund left, Fund right) {
return left.fundValue.compareTo(right.fundValue);
}
});
// sortMe is now sorted
In case of TreeSet either Comparator or Comparable is used to compare and store objects . Equals are not called and that is why it does not recognize the duplicate one
Instead of the TreeSet we can use List and implement the Comparable interface.
public class Fund implements Comparable<Fund> {
String fundCode;
int fundValue;
public Fund(String fundCode, int fundValue) {
super();
this.fundCode = fundCode;
this.fundValue = fundValue;
}
public String getFundCode() {
return fundCode;
}
public void setFundCode(String fundCode) {
this.fundCode = fundCode;
}
public int getFundValue() {
return fundValue;
}
public void setFundValue(int fundValue) {
this.fundValue = fundValue;
}
public int compareTo(Fund compareFund) {
int compare = ((Fund) compareFund).getFundValue();
return compare - this.fundValue;
}
public static void main(String args[]){
List<Fund> funds = new ArrayList<Fund>();
Fund fund1 = new Fund("a",100);
Fund fund2 = new Fund("b",20);
Fund fund3 = new Fund("c",70);
Fund fund4 = new Fund("a",100);
funds.add(fund1);
funds.add(fund2);
funds.add(fund3);
funds.add(fund4);
Collections.sort(funds);
for(Fund fund : funds){
System.out.println("Fund code: " + fund.getFundCode() + " Fund value : " + fund.getFundValue());
}
}
}
Add the elements to the arraylist and then sort the elements using utility Collections.sort,. then implement comparable and write your own compareTo method according to your key.
wont remove duplicates as well, can be sorted also:
List<Integer> list = new ArrayList<>();
Collections.sort(list,new Comparator<Integer>()
{
#Override
public int compare(Objectleft, Object right) {
**your logic**
return '';
}
}
)
;
I found a way to get TreeSet to store duplicate keys.
The problem originated when I wrote some code in python using SortedContainers. I have a spatial index of objects where I want to find all objects between a start/end longitude.
The longitudes could be duplicates but I still need the ability to efficiently add/remove specific objects from the index. Unfortunately I could not find the Java equivalent of the Python SortedKeyList that separates the sort key from the type being stored.
To illustrate this consider that we have a large list of retail purchases and we want to get all purchases where the cost is in a specific range.
// We are using TreeSet as a SortedList
TreeSet _index = new TreeSet<PriceBase>()
// populate the index with the purchases.
// Note that 2 of these have the same cost
_index.add(new Purchase("candy", 1.03));
Purchase _bananas = new Purchase("bananas", 1.45);
_index.add(new Purchase(_bananas);
_index.add(new Purchase("celery", 1.45));
_index.add(new Purchase("chicken", 4.99));
// Range scan. This iterator should return "candy", "bananas", "celery"
NavigableSet<PriceBase> _iterator = _index.subset(
new PriceKey(0.99), new PriceKey(3.99));
// we can also remove specific items from the list and
// it finds the specific object even through the sort
// key is the same
_index.remove(_bananas);
There are 3 classes created for the list
PriceBase: Base class that returns the sort key (the price).
Purchase: subclass that contains transaction data.
PriceKey: subclass used for the range search.
When I initially implemented this with TreeSet it worked except in the case where the prices are the same. The trick is to define the compareTo() so that it is polymorphic:
If we are comparing Purchase to PriceKey then only compare the price.
If we are comparing Purchase to Purchase then compare the price and the name if the prices are the same.
For example here are the compareTo() functions for the PriceBase and Purchase classes.
// in PriceBase
#Override
public int compareTo(PriceBase _other) {
return Double.compare(this.getPrice(), _other.getPrice());
}
// in Purchase
#Override
public int compareTo(PriceBase _other) {
// compare by price
int _compare = super.compareTo(_other);
if(_compare != 0) {
// prices are not equal
return _compare;
}
if(_other instanceof Purchase == false) {
throw new RuntimeException("Right compare must be a Purchase");
}
// compare by item name
Purchase _otherPurchase = (Purchase)_other;
return this.getName().compareTo(_otherChild.getName());
}
This trick allows the TreeSet to sort the purchases by price but still do a real comparison when one needs to be uniquely identified.
In summary I needed an object index to support a range scan where the key is a continuous value like double and add/remove is efficient.
I understand there are many other ways to solve this problem but I wanted to avoid writing my own tree class. My solution seems like a hack and I am surprised that I can't find anything else. if you know of a better way then please comment.
I'm not sure how to word this correctly, but I'm told to write a method that would return the largest course object (the course with the most students). If there are two courses that have the same number of students, it would return both.
The second part of the problem is what troubles me, because I'm not allowed to make another ArrayList other than the ones he specified (which is already used). Is there a way to keep track of two+ objects without using an list/hash?
This is what I've done so far, but it only returns one course object.
public Course largestEnrollment(){
int size = 0;
Course p = null;
for (Integer c : courseList.keySet()){
if (courseList.get(c).getClassList().size() > size){
p = courseList.get(c);
size = courseList.get(c).getClassList().size();
}
return p;
}
return null;
}
Return an array of Course objects:
public Course[] largestEnrollment(){
You'll need to decide how to manipulate the array inside your for loop.
Sort the ArrayList based on size. Then you can return a sub-list of the largest courses.
If you don't have so many Course (e.g. <1k), you could implement Comparable or write a Comparator for your Course object. So that you could just from the map get all Values(Course) in collection, then sort the collection, just from the end of the sorted collection take those elements with same values(size).
I mentioned the size of the collection because sort make the O(n) problem into O(nlogn). But if the size is small, it is a convenient way to go.
Anyway, you have to change the method return type to a collection or an array.
Sort then return a sublist:
public List<Course> largestEnrollment(List<Course> courses) {
Collections.sort(courses, new Comparator<Course>() {
#Override
public int compare(Course o1, Course o2) {
return o1.getClassList().size() - o2.getClassList().size();
}
});
for (int indexOfLargest = 1; indexOfLargest < courses.size(); indexOfLargest ++) {
if (courses.get(indexOfLargest - 1).getClassList().size() > courses.get(indexOfLargest).getClassList().size())
return courses.subList(0, indexOfLargest);
}
return courses;
}
I have a List containing HashMaps. Each HashMap in the list might have multiple key/value pairs. I want to indexOf on the list to find out the index of the element where the passed in HashMap is. However, the problem is that equals method of HashMap looks at all the entire entrySet while comparing. Which is not what I want.
Example:
List<HashMap> benefit = new ArrayList<HashMap>();
HashMap map1 = new HashMap();
map1.put("number", "1");
benefit.add(map1);
HashMap map2 = new HashMap();
map2.put("number", "2");
map2.put("somethingelse", "blahblah"); //1
benefit.add(map2);
HashMap find = new HashMap();
find.put("number", "2");
int index = benefit.indexOf(find);
if (index >= 0)
System.out.println(benefit.get(index).get("number"));
The above code does not print anything because of line with //1.
What do I have to do so that the above code actually prints 2?
Is there a way to implement comparable on the list so that I can define
my own?
I think you're looking for retainAll(), so you can compare only the elements you're interested in:
int index = myIndexOf(benefit, find);
...
static int myIndexOf(List<HashMap> benefit, Map find) {
int i = 0;
for (Map map : benefit) {
Map tmp = new HashMap(map);
tmp.keySet().retainAll(find.keySet());
if (tmp.equals(find)) {
return i;
}
i++;
}
return -1;
}
It's possible, of course, to declare your own subclass of List that overrides the indexOf method with this behaviour. However, I don't think that's a good idea. It would violate the contract of the indexOf method:
returns the lowest index i such that (o==null ? get(i)==null : o.equals(get(i)))
This would be confusing to someone else maintaining the code. You might then think that you could subclass HashMap to redefine equals, but that would violate the symmetry property of Object.equals().
The way you are trying to achieve your goal is wrong. The indexOf method works exactly as it should in this case. It is trying to find an exact match, not a partial one.
What you are trying to do, if I get it correctly, is to find a map in your list of maps that contains a specific entry. In this case, you should manually perform this search, by going through all the maps, calling containsKey (), and then comparing the value you are expecting to find with the value associated with the key.
The other way would be to create a proxy class around your List, and add a new method findMapWithEntry (String key, String value), which would perform this seach for you (the same search I described above).
Why not change the way you search?
List<Map> matchingBenefits = new ArrayList<Map>();
for (Map m : benefit) {
if (m.containsKey("number") && m.get("number").equals("2"))
matchingBenefits.add(m);
}
for (Map m : matchingBenefits) {
System.out.println(m.get("number"));
}
You can always override the indexOf method. Looking at the source for ArrayList:
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
So it's not a very complex search algorithm at all. You may look at something like:
List benefit = new ArrayList(){
public int indexOf(Object o){
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
else {
for (int i = 0; i < size; i++) //traverse the hashmaps
Object key = ((HashMap)o).keySet().get(0); //assuming one pair
Object val = ((HashMap)o).valueSet().get(0);
if (
((HashMap)elementData[i]).containsKey(key) &&
((HashMap)elementData[i]).get(key).equals(val))
return i;
}
return -1;
};
My advice would be to consider a different data structure, perhaps writing your own one for it.
Given that you cannot change the design, would writing your own find method help?
The code below should work if I understood what you're trying to do and it runs in O(n)
public static String find(List<HashMap<String,String>> listMap, String key, String value) {
for(int i = 0; i < listMap.size(); i++)
if(listMap.get(i).get(key).equals(value))
return value;
return null;
}