This question already has answers here:
Iterating through a LinkedHashMap in reverse order
(6 answers)
Closed 7 years ago.
I have a LinkedHashMap (because the order of entry is important).
I want to be able to iterate over it's entries (key, value pairs) like an array
from end to start so the foreach methods doesn't fit here
something like :
for (int i = DestIndexStartPair.entrySet().size() , i> 0 ; i--) {
entry = DestIndexStartPair.entrySet()[i];
String Endsection = output.substring(entry.value());
System.out.println(Endsection );
}
There is no efficient way to iterate over a Set (such as entrySet) in reverse order. If you need iteration in both directions, then for the opposite direction the best approach is to copy into a temporary list and iterate the list in reverse.
Of course, if you only need one direction, then you should just ensure the LinkedHashMap has that order instead of the opposite.
Working solution:
List<Entry<Foo,Bar>> list = new ArrayList<Entry<Foo,Bar>>(map.entries());
for (int i = list.size() - 1; i >= 0; i--) {
Entry<Foo,Bar> entry = list.get(i);
}
Obviously, the trade off here is that you have to copy the whole map into the array list. If your map is too big (whatever is too big), you might have performance and memory problems.
Looking at map implementation might help more. May be by overriding in more appropriate way this can be achieved in balanced way as in performance or thread safety etc. But here is small hack that may help,
public class HackedLinkedHashMap<K,V> extends LinkedHashMap<K,V> implements Map<K,V> {
private LinkedList<K> keyList = new LinkedList<>();
#Override
public V put(K key, V value){
V obj = super.put(key, value);
keyList.addFirst(key);
return obj;
}
public LinkedList<K> getReversedKeyList(){
return keyList;
}}
public static void main(String[] args) {
System.out.println("Test conference");
HackedLinkedHashMap<Integer, Integer> map = new HackedLinkedHashMap();
for(Integer i=0;i<100;i++){
map.put(i, i);
}
Iterator<Integer> iterator = map.getReversedKeyList().iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
An EntrySet is a Set and thus doesn't normally have an index.
So, what you need would be...
for(Entry<K,V> entry : DestIndexStartPair.entrySet()) {
String Endsection = output.substring(entry.value()); // whatever output is
System.out.println(Endsection );
}
...and then store the result, and reverse it.
Related
Its a very trivial question and related to coding Style and I am just asking to make my coding style more readable
Suppose I have a Collection like linkedList and an Array and I need to iterate over both simultaneously.
currently the best way I know is to get a iterator over list and define a index variable outside the iterator loop and increment the index variable simultaneously to access both next elements {list and array}. Please refer the example below
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] arr = new Array[25];
// lets suppose both have 25 elements.
// My Iteration method will be
int index =0;
for (Integer val : list) {
System.out.println(val);
System.out.println(arr[index++]);
}
so is it the only way or is there any other way I can perform this iteration in more readable and more relatable manner, where I don't have to take index variable separately.
I know it can be possible that array might have less or more elements than collection but I am only talking about the cases where they have equal and we need to iterate over Both of them.
PS : anybody can write a code that a computer can understand, actual challenge is to write code which humans can understand easily.
What you have is essentially fine: it's simple, and simple can be sufficient to make code readable.
The only thing I would caution about is the side effect of index++ inside arr[index++]: if, say, you want to use the same value multiple times in the loop body, you couldn't simply copy+paste.
Consider pulling out a variable as the first thing in the loop to store the "current" array element (which is essentially what the enhanced for loop does for the list element).
for (Integer val : list) {
Integer fromArr = arr[index++];
// ...
}
Just to point out an alternative without having a separate variable for the index, you can use ListIterator, which provides you with the index of the element.
// Assuming list and are have same number of elements.
for (ListIterator<Integer> it = list.listIterator();
it.hasNext();) {
// The ordering of these statements is important, because next() changes nextIndex().
Integer fromArr = arr[it.nextIndex()];
Integer val = it.next();
// ...
}
ListIterator is not an especially widely-used class, though; its use may in and of itself be confusing.
One of the downsides of the ListIterator approach is that you have to use the it correctly: you shouldn't touch it inside the loop (after getting the values), you have to put the statements in the right order, etc.
Another approach would be to create a library method analogous to Python's enumerate:
static <T> Iterable<Map.Entry<Integer, T>> enumerate(Iterable<? extends T> iterable) {
return () -> new Iterator<T>() {
int index = 0;
Iterator<? extends T> delegate = iterable.iterator();
#Override public boolean hasNext() { return delegate.hasNext(); }
#Override public Map.Entry<Integer, T> next() {
return new AbstractMap.SimpleEntry<>(index++, delegate.next());
}
};
}
This returns an iterable of map entries, where the key is the index and the value is the corresponding value.
You could then use this in an enhanced for loop:
for (Map.Entry<Integer, Integer> entry : enumerate(list)) {
Integer fromList = entry.getValue();
Integer fromArr = arr[entry.getKey()];
}
One option is to have 2 iterators, but I don't think it is any clearer:
for (Iterator<Integer> i1 = list.iterator(), i2 = Arrays.asList(arr).iterator();
i1.hasNext() && i2.hasNext();) {
System.out.println(i1.next());
System.out.println(i2.next());
}
But it is more robust in that it finishes at the shorter of the 2 collections.
I tried to simplify and handle size wise collections where both need not be of the same size. I believe this would work even if the sizes are not same and just one loop would suffice. Code snippet below:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] arr = new Array[25];
int maxLength= Math.max(list.size(),arr.size());
//Looping over the lengthy collection( could be Linkedlist or arraylist)
for(int i=0;i<maxLength;i++){
if(list.size()>i)
System.out.println(list[i]);
if(arr.size()>i)
System.out.println(arr[i]);
}
Hope this helps! Thanks
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 7 years ago.
I'm trying to have a sorted map Map<Integer,Set<Integer>> which keeps elements sorted based on the size() of the value set.
In practice this is a map of a node to the other nodes connected to that node. I want to quickly (O(logn)) access the node with the most edges without having to sort every time.
For example the order should be:
3 => {1,2,4,5}
12 => {1,2,3}
14 => {3,2,3}
65 => {3,8}
6 => {2}
2 => {5}
Since TreeMap won't do it since I can't sort based on values, I probably need to roll something custom.
EDIT: The size of the Set may indeed change which may over-complicate things even more
What would be the simplest way to achieve this?
Here's a sort example how to use two sets for this. One set is sorted by Set::size, and the other is just a normal Map with an integer index. To use this, you have to keep the same key/value pairs in both maps.
I'm not sure if I'd recommend trying to make a single Map out of this. It's got two lookups, by index and by size, so it doesn't really work like a regular map. It will depend on your use model.
package quicktest;
import static java.util.Comparator.comparing;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest
{
public static void main(String[] args) {
TreeMap<Integer,Set<Integer>> index = new TreeMap<>();
TreeMap<Set<Integer>,Integer> size = new TreeMap<>( comparing( Set::size ) );
for( int i = 0; i < 5; i++ ) {
Set<Integer> set = new HashSet<>();
for( int val = 0; val <= i; val++ ) {
set.add( val );
}
index.put( i, set );
size.put( set, i );
}
System.out.println( size.lastEntry() ); // largest set size
System.out.println( index.get( 2 ) ); // random index
}
}
What about this?
public class MapAndPriority {
Map<Integer, Set<Integer>> sets = new HashMap<Integer, Set<Integer>>();
PriorityQueue<Set<Integer>> byLength = new PriorityQueue<Set<Integer>>(1, new Comparator<Set<Integer>>() {
#Override
public int compare(Set<Integer> o1, Set<Integer> o2) {
// Compare in the reverse order!
return o2.size() - o1.size();
}
});
public void add(int i, Set<Integer> set) {
sets.put(i, set);
byLength.offer(set); // or Add, depending on the behavior you want
}
public Set<Integer> get(int i) {
return sets.get(i);
}
public Set<Integer> mostNodes() {
return byLength.peek();
}
public void remove(int i) {
// sets.remove will return the removed set, so that will be removed from byLength.
// Need to handle case when i does not exist as a key in sets
byLength.remove(sets.remove(i));
}
}
If I understand what you want, then this will:
Add new sets in o(nlog(n))
Regular map get()
Will get the largest set (mostNodes()) in o(log(n))
What I did was to place all sets in a priority queue (along side the map) and then give the priority queue a comparator that compares based on the sizes, so that smaller size is "larger". That way when you call peek() it will return the 'minimum' value in the priority queue, which due to our comparator it will be the longest set.
I didn't deal with all kinds of edge cases (like removing when empty).
You can take a look at the documentation for more details and about the complexity.
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);
I have this HashMap<String, ArrayList<Item>> , is there a way to count the total number of items in all the Lists in the Map without going through all Lists?
Or should I iterate through the Map and along all lists ?
As of Java 8 you accomplish that with an one-liner:
Integer sum = map.values().stream().mapToInt(List::size).sum();
Use the size() method defined for HashMap.
HashMap<String, ArrayList<Item>> hMap = new LinkedHashMap<String, ArrayList<Item>>();
int hSize;
/*
put/remove operations
*/
hSize = hMap.size(); // Gives the total no. of elements of HashMap
You might want to see Google Guava and use Multimap instead of that. The Multimap.size() method will give the answer you want.
You'll need to iterate over the List<Item> values in your Map and count the total. The Map doesn't have any knowledge of what values you're putting into it, so it can't provide a facility to get you a total. The code you need is fairly simple:
int total = 0;
for (List<Item> list : map.values()) {
total += list.size();
}
Since Map itself has no a priori knowledge about the values stored in it, except that they are Objects (thus can't be expected to perform any operations on them, other than calling toString(), equals() and/or hashCode()), there is no other way than iterating through its elements and calculating the sum manually.
Multimap sounds like the right choice however, you could do
public static <K, V> int count(Map<K, ? extends Collection<V>> map) {
int count = 0;
for (Collection<V> coll : map.values()) count += coll.size();
return count;
}
BTW: You may want count to return a long ;)
If Any one still looking out for answers
Here is the code posted
Iterator<Map.Entry<Integer, ArrayList<ShortListedFlats>>> iter = rentShortListedFlats
.entrySet().iterator();
while (iter.hasNext()) {
ArrayList<ShortListedFlats> shortLists = iter.next().getValue();
counter = counter + shortLists.size();
}
Yes, you need a for loop:
public static int countItems(HashMap<String, ArrayList<Item>> yourMap){
int counter = 0;
for(ArrayList<Item>> list: yourMap.values()){
counter += list.size();
}
return counter;
}
How to retrieve an element from HashMap by its position, is it possible at all?
Use a LinkedHashMap and when you need to retrieve by position, convert the values into an ArrayList.
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<String,String>();
/* Populate */
linkedHashMap.put("key0","value0");
linkedHashMap.put("key1","value1");
linkedHashMap.put("key2","value2");
/* Get by position */
int pos = 1;
String value = (new ArrayList<String>(linkedHashMap.values())).get(pos);
HashMaps do not preserve ordering:
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.
Take a look at LinkedHashMap, which guarantees a predictable iteration order.
If you want to maintain the order in which you added the elements to the map, use LinkedHashMap as opposed to just HashMap.
Here is an approach that will allow you to get a value by its index in the map:
public Object getElementByIndex(LinkedHashMap map,int index){
return map.get( (map.keySet().toArray())[ index ] );
}
If you, for some reason, have to stick with the hashMap, you can convert the keySet to an array and index the keys in the array to get the values in the map like so:
Object[] keys = map.keySet().toArray();
You can then access the map like:
map.get(keys[i]);
Use LinkedHashMap:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries.
Use LinkedHashMap and use this function.
private LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
Define like this and.
private Entry getEntry(int id){
Iterator iterator = map.entrySet().iterator();
int n = 0;
while(iterator.hasNext()){
Entry entry = (Entry) iterator.next();
if(n == id){
return entry;
}
n ++;
}
return null;
}
The function can return the selected entry.
By default, java LinkedHasMap does not support for getting value by position. So I suggest go with customized IndexedLinkedHashMap
public class IndexedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private ArrayList<K> keysList = new ArrayList<>();
public void add(K key, V val) {
super.put(key, val);
keysList.add(key);
}
public void update(K key, V val) {
super.put(key, val);
}
public void removeItemByKey(K key) {
super.remove(key);
keysList.remove(key);
}
public void removeItemByIndex(int index) {
super.remove(keysList.get(index));
keysList.remove(index);
}
public V getItemByIndex(int i) {
return (V) super.get(keysList.get(i));
}
public int getIndexByKey(K key) {
return keysList.indexOf(key);
}
}
Then you can use this customized LinkedHasMap as
IndexedLinkedHashMap<String,UserModel> indexedLinkedHashMap=new IndexedLinkedHashMap<>();
TO add Values
indexedLinkedHashMap.add("key1",UserModel);
To getValue by index
indexedLinkedHashMap.getItemByIndex(position);
I'm assuming by 'position' you're referring to the order in which you've inserted the elements into the HashMap. In that case you want to be using a LinkedHashMap. The LinkedHashMap doesn't offer an accessor method however; you will need to write one like
public Object getElementAt(LinkedHashMap map, int index) {
for (Map.Entry entry : map.entrySet()) {
if (index-- == 0) {
return entry.value();
}
}
return null;
}
Another working approach is transforming map values into an array and then retrieve element at index. Test run of 100 000 element by index searches in LinkedHashMap of 100 000 objects using following approaches led to following results:
//My answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.values().toArray(new Particle[map.values().size()])[index];
} //68 965 ms
//Syd Lambert's answer:
public Particle getElementByIndex(LinkedHashMap<Point, Particle> map,int index){
return map.get( (map.keySet().toArray())[ index ] );
} //80 700 ms
All in all retrieving element by index from LinkedHashMap seems to be pretty heavy operation.
HashMap - and the underlying data structure - hash tables, do not have a notion of position. Unlike a LinkedList or Vector, the input key is transformed to a 'bucket' where the value is stored. These buckets are not ordered in a way that makes sense outside the HashMap interface and as such, the items you put into the HashMap are not in order in the sense that you would expect with the other data structures
HashMap has no concept of position so there is no way to get an object by position. Objects in Maps are set and get by keys.
HashMaps don't allow access by position, it only knows about the hash code and and it can retrieve the value if it can calculate the hash code of the key. TreeMaps have a notion of ordering. Linkedhas maps preserve the order in which they entered the map.
you can use below code to get key :
String [] keys = (String[]) item.keySet().toArray(new String[0]);
and get object or list that insert in HashMap with key of this item like this :
item.get(keys[position]);
You can try to implement something like that, look at:
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("juan", 2);
map.put("pedro", 3);
map.put("pablo", 5);
map.put("iphoncio",9)
List<String> indexes = new ArrayList<String>(map.keySet()); // <== Parse
System.out.println(indexes.indexOf("juan")); // ==> 0
System.out.println(indexes.indexOf("iphoncio")); // ==> 3