Maintain a sorted list with performance - java

Summary of my question: I need a list that can quickly be iterated and sorted (either by sorting method or adding/removing object).
I'm coding a game in which there are a lot of "collision zones", that are checked every frame. For optimization, I have a idea of sorting them depends on their X position. The problem is not all collision zones are static, because some of them can move around.
I have managed to handles all the changes, but to maintain the ArrayList (or ConcurrentLinkedQueue) sorted using Collections.sort() is too slow.
So I got a new idea: I may use a Tree, and whenever a zone's X is changed, instead of sorting all elements again, I can just remove then re-add it from the tree. However, I think that adding and removing operator in TreeList are expensive too. Moreover, iterating through Tree is not as effective as ConcurrentLinkedQueue, LinkedList or ArrayList.
Please tell me if there is any built-in data structure that satisfy my need. If there is no such data structure, I intend to extend ArrayList class, override the add method to ensure the order (by using overload add(index, item). If you think this is the best way, please give me the best way to find the index. I already use BinarySearch but I think there is a bug:
#Override
public boolean add(T e) {
// Find the position
int left = 0;
int right = this.size() - 1;
int pos = right / 2;
if (e.compareTo(this.get(0)) <= 0) {
pos = 0;
} else if (e.compareTo(this.get(this.size() - 1)) >= 0) {
pos = this.size();
} else {
// Need: e[pos - 1] <= e[pos] <= e[pos + 1]
boolean firstCondition = false;
boolean secondCondition = false;
do {
firstCondition = this.get(pos - 1).compareTo(this.get(pos)) <= 0;
secondCondition = this.get(pos).compareTo(this.get(pos + 1)) >= 0;
if (!firstCondition) {
right = pos - 1;
pos = (left + right) / 2;
} else if (!secondCondition) {
left = pos + 1;
pos = (left + right) / 2;
}
} while (!(firstCondition && secondCondition));
}
this.add(pos, e);
return true;
}

I would use a tree set. if you need to allow duplicates you can use a custom comparator. while iterating a tree set is slightly slower than an array, adding and removing is much faster.
It appears you are doing an insertion sort which is O (n). an insert on a tree set is O (ln n)
IMHO The best way to store duplicates by using a TreeMap<MyKey, List<MyType>> like this
Map<MyKey, List<MyType>> map = new TreeMap<>();
// to add
MyType type = ...
MyKey key = ...
List<MyType> myTypes = map.get(key);
if (myTypes == null)
map.put(key, myTypes = new ArrayList<>());
myTypes.add(type);
// to remove
MyType type = ...
MyKey key = ...
List<MyType> myTypes = map.get(key);
if (myTypes != null) {
myTypes.remove(myType);
if (myTypes.isEmpty())
map.remove(key);
}
In this case, addition and removal is O(ln N);
You can allow "duplicates" is a TreeSet by defining all objects as different e.g.
Set<MyType> set = new TreeSet<>(new Comparator<MyType>() {
public int compare(MyType o1, MyType o2) {
int cmp = /* compare the normal way */
if (cmp == 0) {
// or use System.identityHashCode()
cmp = Integer.compare(o1.hashCode(), o2.hashCode());
return cmp == 0 ? 1 : cmp; // returning 0 is a bad idea.
}
}
}
As you can see this approach is ugly unless you have some way of making every object unique.

It sounds like you want a TreeSet.

If you intend to use a binary search / insert on an already sorted array or ArrayList then you will get the same big O complexity than a binary tree.
So I recommend that you actuall test the provided tree implementations (i.e. TreeSet), not just guess. As they are not plain tree implementations I would not be surprised if iteration is also fast on them.

Related

Java with ArrayLists the index of function

is there a way with the index of function to return the index of a value that is part of a class like just one field of the bigger structure.. I got a simple contact class and I want to return the index when the id is a certain value .. should I be using a different structure than an arrayList it is doing most of what I want but the index of function is frustrating
I think the easiest way is to just iterate over the array, and check the condition with "if". That will be O(n).
You can use the Predicate method explained here.
Optional<Integer> indexOfMatch = IntStream.range(0, yourList.size())
.filter(i -> valueYouAreLookingFor.equals(yourList.get(i).getFieldYouAreChecking()))
.findFirst();
Or if the field holds a primitive value:
Optional<Integer> indexOfMatch = IntStream.range(0, yourList.size())
.filter(i -> valueYouAreLookingFor == yourList.get(i).getFieldYouAreChecking())
.findFirst();
If there is an element in the list that matches your predicate, indexOfMatch will have the index of that element. If not, indexOfMatch will be an empty Optional.
Regarding whether an ArrayList is the appropriate structure, generally if you are working with a list of values, an implementation of List is what you want. Whether it should be ArrayList or some other implementation depends on details of what you are doing with the list. For small list sizes, it often doesn't really matter which implementation you use.
well I switched to a vector from an array list but it did not really help but I did use a loop and the list size and just put the contact in a container each time so I could do my comparison it was kinda messy but I got the index that way.
static int SearchForContact(String ContactID) {
int temp = -1;
Contact searchCon;
for (int z = 0 ; z < contactVector.size(); z++)
{searchCon = contactVector.get(z);
if(searchCon.getId() == ContactID)
{temp = z;}
}
if (temp == -1) {
throw new IllegalArgumentException("Contact not found");
}
return temp;
}
I am iterating over the ArrayList and seeing whether the current value is the id value you want.
int yourValue = 25; //I am assuming it to be an int of value 25, you can keep it whatever you want.
for (int i = 0; i < yourArrayList.size(); i++)
{
if (yourArrayList.get(i) == yourValue)
{
return i; //returning the index value
}
}
return null; //this will run if the value doesn't exist.
Obviously, this would need to be inside a method that returns an integer that is the index. Above, yourArrayList is the ArrayList you are using, and yourValue is the value you need to find. It doesn't have to be an int.

Sorting a list of collections performance tips

I have a list of collections that contains pairs, I should keep the list sorted alphabetically by it's collections pairs key, My current solution is keeping the list sorted by overriding the add method, Like the code below.
Note: the list collections pairs key are always the same like
(Car,1)(Car,1)
(Bear,1)
So i just need to get first pair key of collections to get it sorting the list
List<Collection<Pair<String, Integer>>> shufflingResult;
public void init() {
shufflingResult = new ArrayList<>() {
public boolean add(Collection<Pair<String, Integer>> c) {
super.add(c);
Collections.sort(shufflingResult, new Comparator<Collection<Pair<String, Integer>>>() {
#Override
public int compare(Collection<Pair<String, Integer>> pairs, Collection<Pair<String, Integer>> t1) {
return pairs.iterator().next().getKey().compareTo(t1.iterator().next().toString());
}
});
return true;
}
};
}
Is this the best performance way to do what i'm looking for?
Performance is a tricky thing. The best sort algorithm will depend largely on the volume and type of data, and to what degree it is random. Some algorithms are best when Data which is partially sorted, others for truly random data.
Generally speaking, worry about optimization until you've determined that working code is not sufficiently performant. Get things working first, and then determine where the bottleneck it. It may not be sorting, but something else.
Java provides good general sorting algorithms. You're using one with Collections.sort(). There is no SortedList in Java, but javafx.base contains a SortedList which wraps a supplied List and keeps is sorted based on a Comparator supplied at instantiation. This would prevent you from having to override the base behavior of the List implementation.
While your code seems like it may work, here's a couple of suggestions:
pairs.iterator().next().getKey() will throw an NPE if pairs is null.
pairs.iterator().next().getKey() will throw an NoSuchElementException if pairs is empty.
pairs.iterator().next().getKey() will throw an NPE if the first Pair has a null key is empty.
All of this is true for t1 as well.
You're comparing pairs.iterator().next().getKey() to t1.iterator().next().toString(). One is the String representation of the Pair, and the other is the Key from the Pair. Is this correct?
While your code may make sure these conditions never happen, someone may modify it later with resulting unpleasant surprises. You may want to add validations to your add method to ensure that these cases won't occur. Throwing IllegalArgumentException when arguments aren't valid is generally good practice.
Another thought: Since your Collection contents are always the same, and if no two Collections have the same kind of Pairs, you should be able to use a SortedMap<String, Collection<Pair<String,Integer>>> instead of a List. If you are comparing by Key this sort of Map will keep things sorted for you. You'll put the Collection using the first Pair's Key as the map/entry key. The map's keySet(), values() and entrySet() will all return Collections which iterate in sorted order.
If the collection is already sorted and all you want to do is add. do a binary search and then just use list.add(index,element); sorting every time you want to insert is bad. you should do it once and then just maintain with good insertion order.
adding some code to show the bsearch. as the one for collections will only return matches. just supply the list. the new object. and the comparator that sorts the list how you want it. if adding many items where N> current size of the list probably better to add all then sort.
private static void add(List<ThingObject> l, ThingObject t, Comparator<ThingObject> c) {
if (l != null) {
if (l.size() == 0) {
l.add(t);
} else {
int index = bSearch(l, t, c);
l.add(index, t);
}
}
}
private static int bSearch(List<ThingObject> l, ThingObject t, Comparator<ThingObject> c) {
boolean notFound = true;
int high = l.size() - 1;
int low = 0;
int look = (low + high) / 2;
while (notFound) {
if (c.compare(l.get(look), t) > 0) {
// it's to the left of look
if (look == 0 || c.compare(l.get(look - 1), t) < 0) {
//is it adjacent?
notFound = false;
} else {
//look again.
high = look - 1;
look = (low + high) / 2;
}
} else if (c.compare(l.get(look), t) < 0) {
// it's to the right of look
if (look == l.size() - 1 || c.compare(l.get(look + 1), t) > 0) {
//is it adjacent?
look = look + 1;
notFound = false;
} else {
//look again.
low = look + 1;
look = (low + high) / 2;
}
} else {
notFound = false;
}
}
return look;
}

How can I tell if the elements in an Array List are same or different?

I'm trying to verify if all the elements in an array list are same or not. This is my code:
ArrayList<Integer> arr = new ArrayList<>(Arrays.asList(2,2,4,2));
for (int z = 0; z < arr.size(); z++) {
if(!arr.get(z++).equals(arr.get(z--))) {
System.out.println("same");
}else {
System.out.println("differnt");
}
}
Put the elements into a Set. If the resulting set has a size of 1, then all elements have been the same. One line of code, no loops, no indices, works with every collection:
boolean allTheSame = new HashSet<Integer>(list).size() == 1;
System.out.println(allTheSame ? "same" : "different");
(Edited:)
It might be worth noting that if the list is large, and likely contains many different elements, then constructing a Set will impose some memory overhead that can be avoided, if desired. In this case, you'd iterate over the list and compare all elements to the first one. But you should not check the elements for identity with ==. Instead, you should compare them using their equals method, or, if you graciously want to handle null entries, using Objects#equals.
An example of how to solve this efficiently and generically is given in the answer by Zabuza
There are various solutions to this.
Compare any with others
You just need to pick any element (the first, for example) and then compare this to all other elements. A single simple loop is enough:
public static <E> areElementsEquals(List<E> list) {
// Edge cases
if (list == null || list.size() <= 1) {
return true;
}
// Pick any element
E any = list.get(0);
// Compare against others
for (E other : list) {
// Use Objects#equals for null-safety
if (!Objects.equals(any, other)) {
return false;
}
}
return true;
}
Or a Stream-API version:
return list.stream()
.allMatch(other -> Objects.equals(any, other));
If you checked that any is not null, you could also use a method reference:
return list.stream()
.allMatch(any::equals);
Set
Sets do not have duplicates. You can put all your elements into a Set and check if the size is 1, then all other elements were duplicates.
return new HashSet<>(list).size() == 1;
While this code is pretty compact, I would favor the more straightforward solution of iterating. It is a bit more readable and also more efficient, since it does not have the additional overhead of setting up a set.
You only have to compare the 1st item against all the others:
int a = arr.get(0);
boolean allSame = true;
for (int z = 1; z < arr.size(); z++) {
allSame = (a == arr.get(z));
if (!allSame) break;
}
if (allSame)
System.out.println("Same");
else
System.out.println("Different");
. . . and does your code work? What sort of output do you get? Are you suffering any exceptions?
Don't declare your List as ArrayList; declare it as List. Don't call a List arr; it isn't an array. Call it numbers or something like that.
Why have you got the bang sign/not operator in line 3? I think that shouldn't be there.
If you think about the different kinds of collection/data structure available, which you can read about here, you will find a collection type whose size() method will tell you how many distinct elements you have.
You just have to compare the current element with the next, if they are different that means you don't have all elements the same:
for(int i = 0; i < list.size() - 1; i++) {
if (list.get(i) != list.get(i + 1)) {
return false; // elements are different
}
}
return true; // all element are the same
Try this :
String first = arr.get(0);
boolean allTheSame = true;
if (arr.size() > 1) {
for (int z = 1; z < arr.size(); z++) {
if (!arr.get(z).equals(first)) {
allTheSame = false;
break;
}
}
}
A method use BitSet to judge are all elements in list is same or not,it need less memory and run faster.
public static boolean areAllElementsSame(List<Integer> numbers) {
BitSet set = new BitSet();
numbers.forEach(new Consumer<Integer>() {
#Override
public void accept(Integer integer) {
set.set(integer);
}
});
return set.cardinality() == 1;
}
This method can also used to figure out how many different elements.
same is a flag that stores the result we intend.
uv is the uniformality variable.
Object is the type of object you stored in list (the arraylist)
import java.util.*;
class Main{
public static void main(String args[]){
ArrayList<Integer> arr = new ArrayList<>(Arrays.asList(2,2,2,2));
boolean same=true;
Object uv=arr.get(0);
for (Object i: arr){
if(!i.equals(uv)){
same=false;
break;
}
}
System.out.print("Result:"+same);
}
}
You will have to check for each element, if all the elements on later indexes are same as that one or different than it.
You can do it using a nested loop like this:
public static void main(String[] args) {
// write your code here
ArrayList<Integer> arr = new ArrayList<Integer>(Arrays.asList(2,2,4,2));
boolean result=true;
for (int i = 0; i < arr.size(); i++) {
for (int j=i; j<arr.size(); j++){
if (!arr.get(i).equals(arr.get(j))){
result=false;
}
}
}
System.out.println(result);
}
the 2nd loop starts from j=i and goes till the right end of the array because you don't need to check the left side of that index as it is already checked in the previous iterations and the result would already have been updated to false.
If you want to ensure that the list contains at least two different elements, you have to "walk" the array once: you compare the first element against all others, and stop on the first mismatch. On mismatch: not all elements are the same, otherwise they are all the same!
But the initial question was a bit unclear. If you want to determine if there are no two equal elements in the array, you have to compare all entries against all others! Then you need two loops: you pick all elemenst in order, to compare them to all others (respectively to all following ones: you already compared slot 1 to all other slots, so you would only have to compare slot 2 to slot3 ... til end).
Another approach would be to use a Set implementation, for example HashSet! Sets have unique members. So when you turn your list into a set, and the set has less entries than the list, you know that the list contains duplicates.

Adding Element to Middle of an Array (Data Structures)

I'm having trouble implementing an add method to an array structure for my data structures class and am not able to find a way to wrap my head around how to do this correctly. I am aware this is an impractical way handling data, but hey data structures.
Instructions:
The add method ensures that this set contains the specified element.
Neither duplicates nor null values are allowed. The method returns
true if this set was modified (i.e., the element was added) and false
otherwise. If the array is full, you must “resize” to an array twice
as large. Note that the constraint on the generic type parameter T of
the ArraySet class ensures that there is a natural order on the values
stored in an ArraySet. You must maintain the internal array in
ascending natural order at all times. The time complexity of the add
method must be O ( N ), where N is the number of elements in the set.
We are able to use the Array and Iterator classes only! Please do not give an answer using arraylists.
My Psudocode is:
Check element exists, check if array is full, if full double size,
compare elements to find an index where we can maintain natural order
and insert element or find the duplicate and exit, copy array from
top down and combine with inserted element and bottom part.
Actual Code:
//Given Fields
T[] elements;
int size;
//Add Method
public boolean add(T element) {
if (element != null) {
int index = -1;
int last_compare = -1;
for (int i = 0; i < size; i++) {
int compare = elements[i].compareTo(element);
if (compare > 0 && last_compare < 0) { //larger element, last element was smaller.
if (isFull()) {
resize(elements.length * 2);
}
index = i;
break;
} else if (compare == 0) {
return false;
}
last_compare = compare;
}
T[] temp = null;
//Need to copy the array here
} else {
return false;
}
}
I don't know how to insert this correctly while maintaining O(N) time complexity.
Any ideas?

Sorted array list in Java

I'm baffled that I can't find a quick answer to this. I'm essentially looking for a datastructure in Java which implements the java.util.List interface, but which stores its members in a sorted order. I know that you can use a normal ArrayList and use Collections.sort() on it, but I have a scenario where I am occasionally adding and often retrieving members from my list and I don't want to have to sort it every time I retrieve a member in case a new one has been added. Can anyone point me towards such a thing which exists in the JDK or even 3rd party libraries?
EDIT: The datastructure will need to preserve duplicates.
ANSWER's SUMMARY: I found all of this very interesting and learned a lot. Aioobe in particular deserves mention for his perseverance in trying to achieve my requirements above (mainly a sorted java.util.List implementation which supports duplicates). I have accepted his answer as the most accurate for what I asked and most thought provoking on the implications of what I was looking for even if what I asked wasn't exactly what I needed.
The problem with what I asked for lies in the List interface itself and the concept of optional methods in an interface. To quote the javadoc:
The user of this interface has precise control over where in the list each element is inserted.
Inserting into a sorted list doesn't have precise control over insertion point. Then, you have to think how you will handle some of the methods. Take add for example:
public boolean add(Object o)
Appends the specified element to the end of this list (optional operation).
You are now left in the uncomfortable situation of either
1) Breaking the contract and implementing a sorted version of add
2) Letting add add an element to the end of the list, breaking your sorted order
3) Leaving add out (as its optional) by throwing an UnsupportedOperationException and implementing another method which adds items in a sorted order.
Option 3 is probably the best, but I find it unsavory having an add method you can't use and another sortedAdd method which isn't in the interface.
Other related solutions (in no particular order):
java.util.PriorityQueue which is probably closest to what I needed than what I asked for. A queue isn't the most precise definition of a collection of objects in my case, but functionally it does everything I need it to.
net.sourceforge.nite.util.SortedList. However, this implementation breaks the contract of the List interface by implementing the sorting in the add(Object obj) method and bizarrely has a no effect method for add(int index, Object obj). General consensus suggests throw new UnsupportedOperationException() might be a better choice in this scenario.
Guava's TreeMultiSet A set implementation which supports duplicates
ca.odell.glazedlists.SortedList This class comes with the caveat in its javadoc: Warning: This class breaks the contract required by List
Minimalistic Solution
Here is a quick and dirty solution.
class SortedArrayList<T> extends ArrayList<T> {
#SuppressWarnings("unchecked")
public void insertSorted(T value) {
int i = Collections.binarySearch((List<Comparable<T>>) this, value);
add(i < 0 ? -i - 1 : i, value);
}
}
Note that despite the binarySearch, insertSorted will run in linear time since add(index, value) runs in linear time for an ArrayList.
Inserting something non-comparable results in a ClassCastException. (This is the approach taken by PriorityQueue as well: A priority queue relying on natural ordering also does not permit insertion of non-comparable objects (doing so may result in ClassCastException).)
A more complete implementation would, just like the PriorityQueue, also include a constructor that allows the user to pass in a Comparator.
Demo
SortedArrayList<String> test = new SortedArrayList<String>();
test.insertSorted("ddd"); System.out.println(test);
test.insertSorted("aaa"); System.out.println(test);
test.insertSorted("ccc"); System.out.println(test);
test.insertSorted("bbb"); System.out.println(test);
test.insertSorted("eee"); System.out.println(test);
....prints:
[ddd]
[aaa, ddd]
[aaa, ccc, ddd]
[aaa, bbb, ccc, ddd]
[aaa, bbb, ccc, ddd, eee]
Overriding List.add
Note that overriding List.add (or List.addAll for that matter) to insert elements in a sorted fashion would be a direct violation of the interface specification.
From the docs of List.add:
boolean add(E e)
    Appends the specified element to the end of this list (optional operation).
Maintaining the sortedness invariant
Unless this is some throw-away code, you probably want to guarantee that all elements remain sorted. This would include throwing UnsupportedOperationException for methods like add, addAll and set, as well as overriding listIterator to return a ListIterator whose set method throws.
Use java.util.PriorityQueue.
You can try Guava's TreeMultiSet.
Multiset<Integer> ms=TreeMultiset.create(Arrays.asList(1,2,3,1,1,-1,2,4,5,100));
System.out.println(ms);
Lists typically preserve the order in which items are added. Do you definitely need a list, or would a sorted set (e.g. TreeSet<E>) be okay for you? Basically, do you need to need to preserve duplicates?
Have a look at SortedList
This class implements a sorted list. It is constructed with a comparator that can compare two objects and sort objects accordingly. When you add an object to the list, it is inserted in the correct place. Object that are equal according to the comparator, will be in the list in the order that they were added to this list. Add only objects that the comparator can compare.
When the list already contains objects that are equal according to the comparator, the new object will be inserted immediately after these other objects.
Aioobe's approach is the way to go. I would like to suggest the following improvement over his solution though.
class SortedList<T> extends ArrayList<T> {
public void insertSorted(T value) {
int insertPoint = insertPoint(value);
add(insertPoint, value);
}
/**
* #return The insert point for a new value. If the value is found the insert point can be any
* of the possible positions that keeps the collection sorted (.33 or 3.3 or 33.).
*/
private int insertPoint(T key) {
int low = 0;
int high = size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = (Comparable<T>) get(mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else {
return mid; // key found
}
}
return low; // key not found
}
}
aioobe's solution gets very slow when using large lists. Using the fact that the list is sorted allows us to find the insert point for new values using binary search.
I would also use composition over inheritance, something along the lines of
SortedList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
It might be a bit too heavyweight for you, but GlazedLists has a SortedList that is perfect to use as the model of a table or JList
You could subclass ArrayList, and call Collections.sort(this) after any element is added - you would need to override two versions of add, and two of addAll, to do this.
Performance would not be as good as a smarter implementation which inserted elements in the right place, but it would do the job. If addition to the list is rare, the cost amortised over all operations on the list should be low.
Just make a new class like this:
public class SortedList<T> extends ArrayList<T> {
private final Comparator<? super T> comparator;
public SortedList() {
super();
this.comparator = null;
}
public SortedList(Comparator<T> comparator) {
super();
this.comparator = comparator;
}
#Override
public boolean add(T item) {
int index = comparator == null ? Collections.binarySearch((List<? extends Comparable<? super T>>)this, item) :
Collections.binarySearch(this, item, comparator);
if (index < 0) {
index = index * -1 - 2;
}
super.add(index+1, item);
return true;
}
#Override
public void add(int index, T item) {
throw new UnsupportedOperationException("'add' with an index is not supported in SortedArrayList");
}
#Override
public boolean addAll(Collection<? extends T> items) {
boolean allAdded = true;
for (T item : items) {
allAdded = allAdded && add(item);
}
return allAdded;
}
#Override
public boolean addAll(int index, Collection<? extends T> items) {
throw new UnsupportedOperationException("'addAll' with an index is not supported in SortedArrayList");
}
}
You can test it like this:
List<Integer> list = new SortedArrayList<>((Integer i1, Integer i2) -> i1.compareTo(i2));
for (Integer i : Arrays.asList(4, 7, 3, 8, 9, 25, 20, 23, 52, 3)) {
list.add(i);
}
System.out.println(list);
I think the choice between SortedSets/Lists and 'normal' sortable collections depends, whether you need sorting only for presentation purposes or at almost every point during runtime. Using a sorted collection may be much more expensive because the sorting is done everytime you insert an element.
If you can't opt for a collection in the JDK, you can take a look at the Apache Commons Collections
Since the currently proposed implementations which do implement a sorted list by breaking the Collection API, have an own implementation of a tree or something similar, I was curios how an implementation based on the TreeMap would perform. (Especialy since the TreeSet does base on TreeMap, too)
If someone is interested in that, too, he or she can feel free to look into it:
TreeList
Its part of the core library, you can add it via Maven dependency of course. (Apache License)
Currently the implementation seems to compare quite well on the same level than the guava SortedMultiSet and to the TreeList of the Apache Commons library.
But I would be happy if more than only me would test the implementation to be sure I did not miss something important.
Best regards!
https://github.com/geniot/indexed-tree-map
I had the same problem. So I took the source code of java.util.TreeMap and wrote IndexedTreeMap. It implements my own IndexedNavigableMap:
public interface IndexedNavigableMap<K, V> extends NavigableMap<K, V> {
K exactKey(int index);
Entry<K, V> exactEntry(int index);
int keyIndex(K k);
}
The implementation is based on updating node weights in the red-black tree when it is changed. Weight is the number of child nodes beneath a given node, plus one - self. For example when a tree is rotated to the left:
private void rotateLeft(Entry<K, V> p) {
if (p != null) {
Entry<K, V> r = p.right;
int delta = getWeight(r.left) - getWeight(p.right);
p.right = r.left;
p.updateWeight(delta);
if (r.left != null) {
r.left.parent = p;
}
r.parent = p.parent;
if (p.parent == null) {
root = r;
} else if (p.parent.left == p) {
delta = getWeight(r) - getWeight(p.parent.left);
p.parent.left = r;
p.parent.updateWeight(delta);
} else {
delta = getWeight(r) - getWeight(p.parent.right);
p.parent.right = r;
p.parent.updateWeight(delta);
}
delta = getWeight(p) - getWeight(r.left);
r.left = p;
r.updateWeight(delta);
p.parent = r;
}
}
updateWeight simply updates weights up to the root:
void updateWeight(int delta) {
weight += delta;
Entry<K, V> p = parent;
while (p != null) {
p.weight += delta;
p = p.parent;
}
}
And when we need to find the element by index here is the implementation that uses weights:
public K exactKey(int index) {
if (index < 0 || index > size() - 1) {
throw new ArrayIndexOutOfBoundsException();
}
return getExactKey(root, index);
}
private K getExactKey(Entry<K, V> e, int index) {
if (e.left == null && index == 0) {
return e.key;
}
if (e.left == null && e.right == null) {
return e.key;
}
if (e.left != null && e.left.weight > index) {
return getExactKey(e.left, index);
}
if (e.left != null && e.left.weight == index) {
return e.key;
}
return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1);
}
Also comes in very handy finding the index of a key:
public int keyIndex(K key) {
if (key == null) {
throw new NullPointerException();
}
Entry<K, V> e = getEntry(key);
if (e == null) {
throw new NullPointerException();
}
if (e == root) {
return getWeight(e) - getWeight(e.right) - 1;//index to return
}
int index = 0;
int cmp;
index += getWeight(e.left);
Entry<K, V> p = e.parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
while (p != null) {
cmp = cpr.compare(key, p.key);
if (cmp > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
} else {
Comparable<? super K> k = (Comparable<? super K>) key;
while (p != null) {
if (k.compareTo(p.key) > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
}
return index;
}
You can find the result of this work at https://github.com/geniot/indexed-tree-map
TreeSet/TreeMap (as well as their indexed counterparts from the indexed-tree-map project) do not allow duplicate keys , you can use 1 key for an array of values. If you need a SortedSet with duplicates use TreeMap with values as arrays. I would do that.
The following method can be used to print the elements of a LinkedList of String objects.
The method accepts a LinkedList object as input and prints each element of the list, separated by a space, to the console.
To make the output more readable, the method also adds a newline before and after the list.
public static void printList(LinkedList<String> list) {
System.out.println("\nList is: ");
for (String element : list) {
System.out.print(element + " ");
}
System.out.println();
}

Categories

Resources