Sorting a HashTable by the values (including alphanumeric) - java

I have a HashTable with alphanumeric values. I want to sort them.
How can I achieve it?

HashTable doesn't preserve the order.
So better Create a List out of it and Sort it.
You need to wrap your types into a class and then implement a Comparator that compares all the types of values (in your term),
class Foo implements Comparator<Foo>{
private int no;
private String alpha;
//+getter/setters
public int compare(Foo f1, Foo f2){
//put your logic here
}
}

Why? You presumably chose HashTable over TreeMap because it had better performance (and no ordering). If you don't want the performance and you do want the ordering, use a TreeMap.

If you don't want to create a new class to hold the key/value relationship and it you are not interested in a TreeMap, then something like the following will also work:
ArrayList<Entry<String,String>> list = new ArrayList<Entry<String,String>>();
list.addAll(map.entrySet());
Collections.sort(list, new Comparator<Entry<String,String>>() {
#Override
public int compare(Entry<String, String> o1, Entry<String, String> o2) {
//your logic here;
}
});

First question - do you really mean sort the values, or do you mean sort the keys?
If you only want to access the sorted values in order once the best way is create a list or array then sort.
For values: Arrays.sort(table.values().toArray()) or Collections.sort(new ArrayList(table.values()))
For keys: Arrays.sort(table.keySet().toArray()) or Collections.sort(new ArrayList(table.keySet()))
For more on these sorting methods: Arrays.sort() or Collections.sort().
If you want to repeatedly use based on sorted keys, you would be better using a TreeMap.
If you repeatedly want to access based on sorted values (rather than keys), then you could always insert in order into a LinkedHashMap, which will keep the ordering.

Related

How to get element position from Java Map

I have this Java Map:
Can you tell me how I can get the 6-th element of the Map?
private static final Map<String, Users> cache = new HashMap<>();
is this possible? Or I have to use another Java collection?
Though a bit late to answer. But the option is to use LinkedHashMap: this map preserves the order according to insertion of elements, as everyone has suggested. However, As a warning, it has a constructor LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) which will create a linked hash map whose order of iteration is the order in which its entries were last accessed. Don't use this constructor for this case.
However, if I needed such functionality, i would extend it and implement my necessary function to re-use them in OOP way.
class MyLinkedMap<K, V> extends LinkedHashMap<K, V>
{
public V getValue(int i)
{
Map.Entry<K, V>entry = this.getEntry(i);
if(entry == null) return null;
return entry.getValue();
}
public Map.Entry<K, V> getEntry(int i)
{
// check if negetive index provided
Set<Map.Entry<K,V>>entries = entrySet();
int j = 0;
for(Map.Entry<K, V>entry : entries)
if(j++ == i)return entry;
return null;
}
}
Now i can instantiate it and can get a entry and value either way i want:
MyLinkedMap<String, Integer>map = new MyLinkedMap<>();
map.put("a first", 1);
map.put("a second", 2);
map.put("a third", 3);
System.out.println(map.getValue(2));
System.out.println(map.getEntry(1));
Output:
3
a second=2
HashMap doesn't grantee the order. If you concern about order you should use LinkedHashMap
Map<String, Users> orderedMap=new LinkedHashMap<>();
Now when you put an element it will keep the order what you put.
If you want to get 6th element, now you can do it since you have your elements in order.
orderedMap.values().toArray()[5]// will give you 6th value in the map.
Example
Map<String, String> orderedMap=new LinkedHashMap<>();
orderedMap.put("a","a");
orderedMap.put("b","b");
System.out.println(orderedMap.values().toArray()[1]); // you will get b(value)
System.out.println(orderedMap.keySet().toArray()[1]); // you will get b(key)
}
A HashMap does not maintain the order of the elements inserted in it. You can used a LinkedHashMap instead which maintains the order of the elements inserted in it.
Though you need to note that even a LinkedHashMap has no such method which would give the element at a particular index. You will have to manually iterate through the entries and extract the element at the 6th iteration.
With guava's Iterables
Iterables.get(map.entrySet(),6);
The HashMap has no defined ordering of keys.It's Unordered.
You can use LinkedHashMap which will store your keys in order of insertion.You can retrieve them by calling keySet().
HashMaps do not preserve ordering:
LinkedHashMap which guarantees a predictable iteration order.
Example
public class Users
{
private String Id;
public String getId()
{
return Id;
}
public void setId(String id)
{
Id = id;
}
}
Users user;
LinkedHashMap<String,Users> linkedHashMap = new LinkedHashMap<String,Users>();
for (int i = 0; i < 3; i++)
{
user = new Users();
user.setId("value"+i);
linkedHashMap.put("key"+i,user);
}
/* Get by position */
int pos = 1;
Users value = (new ArrayList<Users>(linkedHashMap.values())).get(pos);
System.out.println(value.getId());
According to documentation, HashMap is a Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
That's why it is not wise to use this kind of Collection.
UPDATE:
Based on #Prateek implementation of LinkedHashMap I would suggest something like:
LinkedHashMap<String,User> linkedHashMap = new LinkedHashMap<String,User>();
// or LinkedHashMap<String,User> linkedHashMap = new LinkedHashMap<>(); //for java 7+
linkedHashMap.put("1",userObj1);
linkedHashMap.put("2",userObj2);
linkedHashMap.put("3",userObj3);
/* Get by position */
int pos = 1; // Your position
User tmp= (new ArrayList<User>(linkedHashMap.values())).get(pos);
System.out.println(tmp.getName());
A HashMap doesn't have a position. You can iterate through its KeySet or EntrySet, and pick the nth element, but it's not really the same as a position. A LinkedHashMap does have a position, since it has a predictable iteration order.
You need to use a LinkedHashMap in order to be able to tell the order of the inserted elements. HashMap is not capable of doing so.
There is no Order in HashMap. You can obtain the list of may keys using map.keySet() but there's no guarantee the key set will be in the order which you add it in. Use LinkedHashMap instead of HashMap It will always return keys in same order (as insertion)
Correct!!
you will have to use other collection for getting values on index(position).
You can use ArrayList
If the ordering is to mean anything significant, you could consider using a SortedMap.
Concrete implementation: TreeMap
Use LinkedHashMap instead of HashMap It will return keys in same order (as insertion) when calling keySet().
For mare detail about LinkedHashMap see this
For example to get the element from specific index
Create a new list from your values and get the value based on index.
LinkedHashMap<String, List<String>> hMap;
List<List<String>> l = new ArrayList<List<String>>(hMap.values());
l.get(6);

Sort by one field and remove elements by another, in a Map?

Suppose there is NavigableMap<Key, Value> where Key would be:
class Key {
private String keySort;
private String keyRemove;
//getters, setters etc.
#Override
public boolean equals(Object o) {
//only include keyRemove
}
#Override
public int hashCode() {
//only include keyRemove
}
}
And there would also be a comparator:
class SortComparator implements Comparator<Key> {
#Override
public int compare(Key o1, Key o2) {
return o1.getKeySort().compareTo(o2.getKeySort());
}
}
Now the map instantiation would look like this: NavigableMap<Key, Value> myMap = new TreeMap<>(new SortComparator()); What I want is to have inside the Map, the entries sorted only by the keySort and to remove them only based on keyRemove.
The problem is that when I try to remove elements from the map, having only the keyRemove value, it does not work as expected (NullPointerException in Comparator). How can the above implementation be fixed in order to work with the expected behavior, or how can the expected behavior be implemented otherwise?
Edit: I understand what's wrong; the question remains still, for alternatives. From TreeMap JavaDoc:
Note that the ordering maintained by a sorted map (whether or not an
explicit comparator is provided) must be consistent with equals if
this sorted map is to correctly implement the Map interface. (See
Comparable or Comparator for a precise definition of consistent with
equals.)
I am thinking that when you want to remove an entry in a map, the remove method will do the job based on the equals method:
(key==null ? k==null : key.equals(k))
like described here http://docs.oracle.com/javase/6/docs/api/java/util/AbstractMap.html#remove%28java.lang.Object%29.
One solution would be to implement your own remove method by iterating through all the keys in the keyset - searching for the removeKey, but that that will replace the O(1) complexity with O(n). Maybe this can be an workaround for you.

sorting arrayLists

I have a scenario where I have two ArrayLists
ArrayList<String> sortedArrayList
ArrayList<String> unSortedArrayList
I have to sort unSortedArrayList depending on the sortedArrayList.
i.e, sortedArrayList is already sorted, now based on sortedArrayList, I have to sort unSortedArrayList.
unSortedArrayList size is <= to the size of sortedArrayList.
Is there a Java API for that?
Any help is appreciated.
Using Google Guava's excellent Ordering class:
Collections.sort(unSortedArrayList, Ordering.explicit(sortedArrayList));
EDIT You can also do
List<whatever> sortedList = Ordering.explicit(sortedArrayList).immutableSortedCopy(unsortedArrayList);
As I understand what you have is that each element in list 1 has a corresponding element in list 2, and you want list 2 sorted into the order of the 'corresponding' elements. Your best approach is to create an object to contain both Strings:
class StringPair {
String s1;
String s2;
}
Now make an array list of StringPairs and sort it based on the value of s1.
List<String> newSortedList = new ArrayList<String>();
for(String currentSortedStr:sortedList){
if(unsortedList.size==0)break;
if(unsortedList.remove(currentSortedStr)){
newSortedList.add(currentSortedStr);
}
}
You can do something like this if you mean what #Sam Dufel says in the comment
As far as I know there is not such API method for this case.
This is not gonna take care of duplicates. remove will remove only the first occurence of that object. At the if the unsorted list size is greater than 0, you can say it contains duplicates. And if you need duplicates as well, you may wanna add some code to handle that case as well.
or if you mean the normal sorting;
Collections.sort(List<T>) will do the sorting for you.
Another way of doing it;
Collections.sort(unsortedList,new CustomComparator(sortedList));
public class CustomComparator implements Comparator<String>{
private List<String> sortedList;
public CustomComparator(List<String> sortedList){
this.sortedList = sortedList;
}
#Override
public int compare(String o1, String o2) {
return sortedList.indexOf(o1)-sortedList.indexOf(o2);
}
}
Although your question is not clear enough I think that the following will help you.
You can use Collections.sort() to sort list. If you need some custom modification to sort mechanism implement your own Comparator and use 2 args version of this method: Collections.sort(list, comparable)

How can I create a sorted list of integer and string pairs?

How can I create a list (or some other type of container) of integer and strings pairs that allows duplicates in both pairs and can be sorted by the integer value?
I need to fill a container with names (string) and scoring (integer) pairs, the container must allow duplicated values in both name and scoring, and i need to sort this list by the scoring value.
I tried with a SortedMap but doesn't allow duplicated values:
SortedMap<Integer,String> sm=new TreeMap<Integer, String>();
sm.put(23, "Peter");
sm.put(11, "Tony");
sm.put(110, "Claire");
sm.put(13, "ferca");
sm.put(55, "Julian");
sm.put(13, "Pedro");
In this example, ferca and Pedro have the same scoring value, this is something I need to allow, but the SortedMap overwrites "ferca" with "Pedro".
What is the best container type to do this?
Since you want your collection to be ordered, I suggest you use a List and Collections.sort. If you decide to go for this approach you still have two options:
Create a custom Comparator that can be passed as an argument to sort, or
Let the auxiliary Score class implement Comparable<Score>
Here is an example and ideone demo of the latter approach:
import java.util.*;
class Score implements Comparable<Score> {
int score;
String name;
public Score(int score, String name) {
this.score = score;
this.name = name;
}
#Override
public int compareTo(Score o) {
return score < o.score ? -1 : score > o.score ? 1 : 0;
}
}
public class Test {
public static void main(String[] args){
List<Score> scores = new ArrayList<Score>();
scores.add(new Score(23, "Peter"));
scores.add(new Score(11, "Tony"));
scores.add(new Score(110, "Claire"));
scores.add(new Score(13, "ferca"));
scores.add(new Score(55, "Julian"));
scores.add(new Score(13, "Pedro"));
Collections.sort(scores);
}
}
Create a class that enclose these two field
create a custom Comparator that compare two Objects based on int value.
Create a list of that objects
Collection.sort(); pass obj of comparator here
class MyEntity{
int val;
String name;
}
List<MyEntity> list = new ArrayList<MyEntity>();
list.add(new MyEntity(1,"a"));
list.add(new MyEntity(4,"z"));
list.add(new MyEntity(2,"x"));
Collections.sort(list,new MyComparator());
class MyComparator implements Comparator<MyEntity>{
public int compare(MyEntity ob1, MyEntity ob2){
return ob1.getVal() - ob2.getVal() ;
}
}
Note: This is just model to show the basic idea
Here is working ideone Demo
Sounds like a job for Guava's Multimap types, specifically TreeMultimap.
If you want a list, use a list...
The best option would probably be to create your own type to encapsulate the string and the integer, add your own comparison, and put them in an ArrayList<T>.
Sort it when you need to with Collections.sort.
If you don't need to allow duplicates which have the same name and score, you could use a SortedSet instead, so long as your comparison order sorts on both score and name.
After you create a holding type, an alternative structure is PriorityQueue to hold the items. This differs from Collections.sort() because the items are inserted in order, with either the high or low values rising to the top.
The only thing you have to do is write a Comparator to pass onto the PriorityQueue on instanciation, so it knows to sort the items based on the integer value.
Both this method and Collections.sort() deliver the same results with different ways of going about it. They also run in O(N log N) time.

Java Hashmap/Hashtable and numbering

the question is simple - I have to implement JTree TreeModel interface which requires that every object has a number. The tree will represent data that are kept in hashmap/hashtable. Keys in that hashmap are client objects and values are arrays of resources (or ArrayLists) so numbering is only a problem at the top level. What would be the easiest way to number keys in Hashmap/Hashtable?
public class IndexedMap<V> extends HashMap<Long, V> {
private AtomicLong index = new AtomicLong();
public void put(V value) {
put(index.getAndIncrement(), value);
}
}
IndexedMap<Object> objects = new IndexedMap<Object>();
objects.put("foo");
objects.put("bar");
// ...
But why don't you just use an ArrayList? It holds objects by an index, exactly what you need.
Sounds like the user-object keys need to be ordered - their "number" would be derived from their spot in the ordering.
Are the keys Comparable? If so, maybe use a TreeMap. If not, I suppose insertion order is your best bet (LinkedHashMap)

Categories

Resources