I would like to apply a function to a Java collection, in this particular case a map. Is there a nice way to do this? I have a map and would like to just run trim() on all the values in the map and have the map reflect the updates.
With Java 8's lambdas, this is a one liner:
map.replaceAll((k, v) -> v.trim());
For the sake of history, here's a version without lambdas:
public void trimValues(Map<?, String> map) {
for (Map.Entry<?, String> e : map.entrySet()) {
String val = e.getValue();
if (val != null)
e.setValue(val.trim());
}
}
Or, more generally:
interface Function<T> {
T operate(T val);
}
public static <T> void replaceValues(Map<?, T> map, Function<T> f)
{
for (Map.Entry<?, T> e : map.entrySet())
e.setValue(f.operate(e.getValue()));
}
Util.replaceValues(myMap, new Function<String>() {
public String operate(String val)
{
return (val == null) ? null : val.trim();
}
});
I don't know a way to do that with the JDK libraries other than your accepted response, however Google Collections lets you do the following thing, with the classes com.google.collect.Maps and com.google.common.base.Function:
Map<?,String> trimmedMap = Maps.transformValues(untrimmedMap, new Function<String, String>() {
public String apply(String from) {
if (from != null)
return from.trim();
return null;
}
}
The biggest difference of that method with the proposed one is that it provides a view to your original map, which means that, while it is always in sync with your original map, the apply method could be invoked many times if you are manipulating said map heavily.
A similar Collections2.transform(Collection<F>,Function<F,T>) method exists for collections.
Whether you can modify your collection in-place or not depends on the class of the objects in the collection.
If those objects are immutable (which Strings are) then you can't just take the items from the collection and modify them - instead you'll need to iterate over the collection, call the relevant function, and then put the resulting value back.
Might be overkill for something like this, but there are a number of really good utilities for these types of problems in the Apache Commons Collections library.
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "a ");
map.put("key2", " b ");
map.put("key3", " c");
TransformedMap.decorateTransform(map,
TransformerUtils.nopTransformer(),
TransformerUtils.invokerTransformer("trim"));
I highly recommend the Jakarta Commons Cookbook from O'Reilly.
I ended up using a mutation of #erickson's answer, mutated to:
return a new Collection, not modify in place
return Collections with elements of type equal to the return type of the Function
support mapping over either the values of a map or the elements of a list
Code:
public static interface Function<L, R> {
L operate(R val);
}
public static <K, L, R> Map<K, L> map(Map<K, R> map, Function<L, R> f) {
Map<K, L> retMap = new HashMap<K, L>();
for (Map.Entry<K, R> e : map.entrySet()) retMap.put(e.getKey(), f.operate(e.getValue()));
return retMap;
}
public static <L, R> List<L> map(List<R> list, Function<L, R> f) {
List<L> retList = new ArrayList<L>();
for (R e : list) retList.add(f.operate(e));
return retList;
}
You'll have to iterate over all the entries and trim each String value. Since String is immutable you'll have to re-put it in the map. A better approach might be to trim the values as they're placed in the map.
I have come up with a "Mapper" class
public static abstract class Mapper<FromClass, ToClass> {
private Collection<FromClass> source;
// Mapping methods
public abstract ToClass map(FromClass source);
// Constructors
public Mapper(Collection<FromClass> source) {
this.source = source;
}
public Mapper(FromClass ... source) {
this.source = Arrays.asList(source);
}
// Apply map on every item
public Collection<ToClass> apply() {
ArrayList<ToClass> result = new ArrayList<ToClass>();
for (FromClass item : this.source) {
result.add(this.map(item));
}
return result;
}
}
That I use like that :
Collection<Loader> loaders = new Mapper<File, Loader>(files) {
#Override public Loader map(File source) {
return new Loader(source);
}
}.apply();
You could also take a look at Google Collections
Related
The following method performs ordering.
public List<Comparator<Entity>> sort(Map<String, String> map) {
List<Comparator<Entity>> list = new ArrayList<Comparator<Entity>>();
for (Map.Entry<String, String> entry : map.entrySet()) {
boolean sortOrder = entry.getValue().equalsIgnoreCase("asc");
switch (entry.getKey()) {
case "id":
list.add(sortOrder ? Comparator.comparing(Entity::getId) : Comparator.comparing(Entity::getId, Comparator.reverseOrder()));
break;
case "size":
list.add(sortOrder ? Comparator.comparing(Entity::getSize) : Comparator.comparing(Entity::getSize, Comparator.reverseOrder()));
//break;
}
}
return list;
}
The list being returned by the above method is used in the following way.
// map is initialized somewhere based on client's interactions with sorting.
// Based on client's interactions, map may be empty or it may contain one or more ordering fields.
if (MapUtils.isNotEmpty(map)) { // map = new LinkedHashMap<String, String>();
List<Comparator<Entity>> comparators = sort(map);
Comparator<Entity> comparator = comparators.remove(0);
for (Comparator<Entity> c : comparators) {
comparator = comparator.thenComparing(c);
}
list = list.stream().sorted(comparator).collect(Collectors.toList());
} else {
// This is the default ordering.
list = list.stream().sorted(Comparator.comparing(Entity::getId).reversed()).collect(Collectors.toList());
}
Entity contains two fields named id of type Integer and size of type BigDecimal and list is a type of List<Entity>.
Since there are several other classes having the same fields with the same datatypes, I want this method to be generic so that it has to be defined only once like so,
public <T extends Object> List<Comparator<T>> sort(Map<String, String> map, Class<T> clazz) {
List<Comparator<T>> list = new ArrayList<Comparator<T>>();
// Sorting logic.
return list;
}
But doing so, expressions like T::getId will not compile as obvious, since the generic type parameter T evaluates to Object.
Is there a way to code sorting without knowing the actual class type so that this method can be prevented from being repeated everywhere, when it is needed?
A simple way, without having to rely on reflection magic, is to introduce a common interface for all the types having the same fields with the same datatypes as Entity.
Consider the following IdSize interface with the following Entity.
interface IdSize {
Integer getId();
BigDecimal getSize();
}
class Entity implements IdSize {
private Integer id;
private BigDecimal size;
#Override
public Integer getId() {
return id;
}
#Override
public BigDecimal getSize() {
return size;
}
}
Then you can make your method generic like this:
public <T extends IdSize> List<Comparator<T>> sort(Map<String, String> map) {
List<Comparator<T>> list = new ArrayList<Comparator<T>>();
for (Map.Entry<String, String> entry : map.entrySet()) {
boolean sortOrder = entry.getValue().equalsIgnoreCase("asc");
Comparator<T> comparator = null;
switch (entry.getKey()) {
case "id":
comparator = Comparator.comparing(IdSize::getId);
break;
case "size":
comparator = Comparator.comparing(IdSize::getSize);
break;
default: // do something here, throw an exception?
}
list.add(sortOrder ? comparator : comparator.reversed());
}
return list;
}
(I refactored a little the switch-case statement to remove the duplicated code.). Also, you might want to add a default clause.
Use interfaces:
public interface Sizable {
BigDecimal getSize();
}
public interface Id {
int getId();
}
Have your classes implement those interface and use them in your generic methods:
public <T extends Id & Sizable> List<Comparator<T>> sort(Map<String, String> map) {
// ...
}
You'll probably need something more dynamic. Some annotations may help
class Shoe
#Column("id")
#Sortable
public int getId(){ ... }
#Column("Description")
public String getDescription(){...}
Given any class, you can reflect on columns to display, columns that can be sorted ("id", ...), and values of columns ("getId()", ...).
If you want to create a compound Comparator anyway, there is no point in filling a List first. Just do it in one operation:
public static <T> Comparator<T> getOrdering(
Map<String, String> map, Map<String,Comparator<T>> defined) {
return map.entrySet().stream().map(e -> {
Comparator<T> c=defined.get(e.getKey());
return e.getValue().equalsIgnoreCase("asc")? c: c.reversed();
})
.reduce(Comparator::thenComparing)
.orElseThrow(()->new IllegalArgumentException("empty"));
}
This works for arbitrary types but requires to provide a map of existing comparators for a type. But this map isn’t a restriction, it actually improves the operation as it removes the hardcoded set of existing named property comparators. You can use it with an arbitrary type, Entity being exemplary here, as follows:
Map<String,Comparator<Entity>> map=new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
map.put("id", Comparator.comparing(Entity::getID));
map.put("size", Comparator.comparing(Entity::getSize));
Comparator<Entity> cmp=getOrdering(param, map);
whereas param is the ordered map of your question, mapping from property name to either "asc" or "desc". The map holding the predefined comparators can be created once in initialization code and then be re-used.
The creation code doesn’t look so complicated that it deserves implementing a dynamic solution, however, if you still wish to do it, here is the code to generate such a map for arbitrary classes:
public final class DynamicComparators<T> {
public static <T> Map<String,Comparator<T>> getComparators(Class<T> cl) {
return CACHE.get(cl).cast(cl).comps;
}
private static final ClassValue<DynamicComparators> CACHE
=new ClassValue<DynamicComparators>() {
#Override protected DynamicComparators computeValue(Class<?> type) {
return new DynamicComparators<>(type);
}
};
private final Class<T> theClass;
private final Map<String, Comparator<T>> comps;
private DynamicComparators(Class<T> cl) {
theClass=cl;
Map<String,Comparator<T>> map=new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
BeanInfo bi=Introspector.getBeanInfo(cl);
MethodHandles.Lookup l=MethodHandles.lookup();
MethodType invoked=MethodType.methodType(Function.class);
for(PropertyDescriptor pd: bi.getPropertyDescriptors()) {
Method m=pd.getReadMethod();
if(m==null) continue;
Class<?> t=m.getReturnType();
if(!t.isPrimitive() && !Comparable.class.isAssignableFrom(t))
continue;
MethodHandle mh=l.unreflect(m);
MethodType mt=mh.type();
#SuppressWarnings("unchecked")Comparator<T> cmp
= Comparator.comparing((Function<T,Comparable>)LambdaMetafactory
.metafactory(l, "apply", invoked, mt.generic(), mh, mt)
.getTarget().invokeExact());
map.put(pd.getName(), cmp);
}
} catch(Throwable ex) {
throw new RuntimeException(ex);
}
this.comps=Collections.unmodifiableMap(map);
}
#SuppressWarnings("unchecked") <U> DynamicComparators<U> cast(Class<U> cl) {
if(cl!=theClass) throw new ClassCastException();
return (DynamicComparators<U>)this;
}
}
This question already has answers here:
java collection that has key/value pair, and is ordered according to insert order
(4 answers)
Closed 10 years ago.
I am working on a project and I need to store key value pairs (one-to-one mapping) in an ordered fashion. Then I should be able to retrieve the key using the value and value using the key. I have looked at Maps, Sets and Hash Tables, but they aren't ordered.
Also, though trivial, it would be great if we could DS retrieve the keys and values at once i.e., the interface supports such functions.
EDIT: The keys and values are all unique. Maintaining the inserted order is good enough.
Notice that you don't define what counts as "ordered". A LinkedHashMap enables iterating over the keys (and therefore values) in insertion-order. Conversely, a TreeMap lets you specify a sort order with a comparator, and ensures all items added to the map are stored in sorted order. 99 times out of 100, one of these classes should be all you need. Alternatively, Google's Guava project has several very nice BiMap implementations that you may find fits your needs.
I strongly caution you: if you think you need more than what these classes can provide, you are likely over-engineering your problem.
For reasons I can't fully justify, I implemented a proper UniqueOrderedBiMap for you, which is compatible with the Java Collections framework and all implemented functions run efficiently. You can use whatever underlying map you see fit (including an un-ordered map, if you really wanted) and keys and values are always unique. Notice that it is a very thin wrapper around a LinkedHashMap, because that's all you need, a LinkedHashMap with extra checks to ensure Values remain unique.
For the curious, check this answers revision history for a UniqueOrderedMap which lacks the getKey() and removeKey() methods, but more properly implements the Map interface, and only needs a HashSet, rather than a HashMap, to store the known values.
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class UniqueOrderedBiMap<K, V>implements Map<K, V> {
private Map<K, V> orderedMap;
private HashMap<V, K> valueMap;
public UniqueOrderedBiMap() {
this(new LinkedHashMap<K,V>());
}
public UniqueOrderedBiMap(Map<K, V> underlyingMap) {
orderedMap = underlyingMap;
valueMap = new HashMap<V, K>(orderedMap.size());
for(Map.Entry<K, V> e : orderedMap.entrySet()) {
if(!valueMap.containsKey(e.getValue())) { // Duplicate value
// could instead fail softly by removing the associated item from the map, but this seems cleaner/clearer.
// generally this constructor should be passed an empty map anyways
throw new IllegalArgumentException("Duplicate value "+e.getValue()+" found in underlying map.");
}
valueMap.put(e.getValue(), e.getKey());
}
}
#Override
public int size() {
return orderedMap.size();
}
#Override
public boolean isEmpty() {
return orderedMap.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return orderedMap.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
// more efficient than iterating over the map
return valueMap.containsKey(value);
}
#Override
public V get(Object key) {
return orderedMap.get(key);
}
public K getKey(V value) {
return valueMap.get(value);
}
// Likely want to implement a forcePut(K, V) method like Guava's BiMaps do
#Override
public V put(K key, V value) {
if(valueMap.containsKey(value)) {
throw new IllegalArgumentException("Cannot insert non-unique value "+value);
}
V ret = orderedMap.put(key, value);
valueMap.remove(ret);
valueMap.put(value, key);
return ret;
}
#Override
public V remove(Object key) {
V ret = orderedMap.remove(key);
valueMap.remove(ret);
return ret;
}
public K removeKey(V value) {
K ret = valueMap.remove(value);
orderedMap.remove(ret);
return ret;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
// Existing Map implementation's putAll have some optimizations we
// could take advantage of, but this isn't unreasonable for a first pass
for(Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
#Override
public void clear() {
orderedMap.clear();
valueMap.clear();
}
#Override
public Set<K> keySet() {
return orderedMap.keySet();
}
#Override
public Collection<V> values() {
return orderedMap.values();
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return orderedMap.entrySet();
}
#Override
public boolean equals(Object o) {
if(o instanceof UniqueOrderedBiMap) {
UniqueOrderedBiMap<?,?> map = (UniqueOrderedBiMap<?,?>)o;
return orderedMap.equals(map.orderedMap);
}
return false;
}
#Override
public int hashCode() {
return orderedMap.hashCode();
}
#Override public String toString() {
return orderedMap.toString();
}
public static void main(String[] args) {
String[] names = { "Marcus", "Jim", "Tom", "Sam" };
String[] grades = { "A", "B", "D", "F" };
UniqueOrderedBiMap<String,String> insertionMap = new UniqueOrderedBiMap<>();
UniqueOrderedBiMap<String,String> sortedMap = new UniqueOrderedBiMap<>(new TreeMap<String,String>());
for(int i = 0; i < names.length; i++) {
insertionMap.put(names[i], grades[i]);
sortedMap.put(names[i], grades[i]);
}
// Poor man's assert
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=D, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=D}"));
insertionMap.put("Tom", "C");
sortedMap.put("Tom", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=C, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=C}"));
try {
insertionMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
try {
sortedMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
insertionMap.remove("Tom");
sortedMap.remove("Tom");
insertionMap.put("Sam", "C");
sortedMap.put("Sam", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=C}"));
insertionMap.removeKey("A");
sortedMap.removeKey("A");
System.out.println(insertionMap.toString().equals("{Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Sam=C}"));
}
}
If you can use third party libraries then consider using an ImmutableBiMap. Its a Guava Collection class that provides
User specified iteration order
Normal mapping from keys to values and inverse mapping from values to keys
The one consideration is that once created the map is immutable and cannot be modified.
LinkedHashMap should be suitable for you. Read through the link
You will need two LinkedHashMap. You can create custom class that internally uses two LinkedHashMap. One for mapping keys to value and another one for mapping values to key.
I am using this code to convert a Set to a List:
Map<String, List<String>> mainMap = new HashMap<>();
for (int i=0; i < something.size(); i++) {
Set<String> set = getSet(...); //returns different result each time
List<String> listOfNames = new ArrayList<>(set);
mainMap.put(differentKeyName, listOfNames);
}
I want to avoid creating a new list in each iteration of the loop. Is that possible?
You can use the List.addAll() method. It accepts a Collection as an argument, and your set is a Collection.
List<String> mainList = new ArrayList<String>();
mainList.addAll(set);
EDIT: as respond to the edit of the question.
It is easy to see that if you want to have a Map with Lists as values, in order to have k different values, you need to create k different lists.
Thus: You cannot avoid creating these lists at all, the lists will have to be created.
Possible work around:
Declare your Map as a Map<String,Set> or Map<String,Collection> instead, and just insert your set.
Use constructor to convert it:
List<?> list = new ArrayList<>(set);
Also from Guava Collect library, you can use newArrayList(Collection):
Lists.newArrayList([your_set])
This would be very similar to the previous answer from amit, except that you do not need to declare (or instanciate) any list object.
We can use following one liner in Java 8:
List<String> list = set.stream().collect(Collectors.toList());
Here is one small example:
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("A");
set.add("B");
set.add("C");
List<String> list = set.stream().collect(Collectors.toList());
}
the simplest solution
I wanted a very quick way to convert my set to List and return it, so in one line I did
return new ArrayList<Long>(mySetVariable);
Since it hasn't been mentioned so far, as of Java 10 you can use the new copyOf factory method:
List.copyOf(set);
From the Javadoc:
Returns an unmodifiable List containing the elements of the given Collection, in its iteration order.
Note that this creates a new list (ImmutableCollections$ListN to be precise) under the hood by
calling Collection#toArray() on the given set and then
putting these objects into a new array.
I would do :
Map<String, Collection> mainMap = new HashMap<String, Collection>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...); //return different result each time
mainMap.put(differentKeyName,set);
}
You could use this one line change: Arrays.asList(set.toArray(new Object[set.size()]))
Map<String, List> mainMap = new HashMap<String, List>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...);
mainMap.put(differentKeyName, Arrays.asList(set.toArray(new Object[set.size()])));
}
Java 8 provides the option of using streams and you can get a list from Set<String> setString as:
List<String> stringList = setString.stream().collect(Collectors.toList());
Though the internal implementation as of now provides an instance of ArrayList:
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
but JDK does not guarantee it. As mentioned here:
There are no guarantees on the type, mutability, serializability, or
thread-safety of the List returned; if more control over the returned
List is required, use toCollection(Supplier).
In case you want to be sure always then you can request for an instance specifically as:
List<String> stringArrayList = setString.stream()
.collect(Collectors.toCollection(ArrayList::new));
For the sake of completeness...
Say that you really do want to treat the Map values as Lists, but you want to avoid copying the Set into a List each time.
For instance, maybe you are calling one library function that creates a Set, but you are passing your Map<String, List<String>> result to a (poorly-designed but out of your hands) library function that only takes Map<String, List<String>>, even though somehow you know that the operations it does with the Lists are equally applicable to any Collection (and thus any Set). And for some reason you need to avoid the speed/memory overhead of copying each Set to a List.
In this super niche case, depending on the (maybe unknowable) behavior the library function needs out of your Lists, you may be able to create a List view over each Set. Note that this is inherently unsafe (because the library function's requirements from each List could presumably change without you knowing), so another solution should be preferred. But here's how you'd do it.
You'd create a class that implements the List interface, takes a Set in the constructor and assigns that Set to a field, and then uses that internal Set to implement the List API (to the extent possible, and desired).
Note that some List behavior you simply will not be able to imitate without storing the elements as a List, and some behavior you will only partially be able to imitate. Again, this class is not a safe drop-in replacement for Lists in general. In particular, if you know that the use case requires index-related operations or MUTATING the List, this approach would go south very fast.
public class ListViewOfSet<U> implements List<U> {
private final Set<U> wrappedSet;
public ListViewOfSet(Set<U> setToWrap) { this.wrappedSet = setToWrap; }
#Override public int size() { return this.wrappedSet.size(); }
#Override public boolean isEmpty() { return this.wrappedSet.isEmpty(); }
#Override public boolean contains(Object o) { return this.wrappedSet.contains(o); }
#Override public java.util.Iterator<U> iterator() { return this.wrappedSet.iterator(); }
#Override public Object[] toArray() { return this.wrappedSet.toArray(); }
#Override public <T> T[] toArray(T[] ts) { return this.wrappedSet.toArray(ts); }
#Override public boolean add(U e) { return this.wrappedSet.add(e); }
#Override public boolean remove(Object o) { return this.wrappedSet.remove(o); }
#Override public boolean containsAll(Collection<?> clctn) { return this.wrappedSet.containsAll(clctn); }
#Override public boolean addAll(Collection<? extends U> clctn) { return this.wrappedSet.addAll(clctn); }
#Override public boolean addAll(int i, Collection<? extends U> clctn) { throw new UnsupportedOperationException(); }
#Override public boolean removeAll(Collection<?> clctn) { return this.wrappedSet.removeAll(clctn); }
#Override public boolean retainAll(Collection<?> clctn) { return this.wrappedSet.retainAll(clctn); }
#Override public void clear() { this.wrappedSet.clear(); }
#Override public U get(int i) { throw new UnsupportedOperationException(); }
#Override public U set(int i, U e) { throw new UnsupportedOperationException(); }
#Override public void add(int i, U e) { throw new UnsupportedOperationException(); }
#Override public U remove(int i) { throw new UnsupportedOperationException(); }
#Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
#Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
#Override public ListIterator<U> listIterator() { throw new UnsupportedOperationException(); }
#Override public ListIterator<U> listIterator(int i) { throw new UnsupportedOperationException(); }
#Override public List<U> subList(int i, int i1) { throw new UnsupportedOperationException(); }
}
...
Set<String> set = getSet(...);
ListViewOfSet<String> listOfNames = new ListViewOfSet<>(set);
...
I create simple static method:
public static <U> List<U> convertSetToList(Set<U> set)
{
return new ArrayList<U>(set);
}
... or if you want to set type of List you can use:
public static <U, L extends List<U>> List<U> convertSetToList(Set<U> set, Class<L> clazz) throws InstantiationException, IllegalAccessException
{
L list = clazz.newInstance();
list.addAll(set);
return list;
}
Recently I found this:
ArrayList<T> yourList = Collections.list(Collections.enumeration(yourSet<T>));
I found this working fine and useful to create a List from a Set.
ArrayList < String > L1 = new ArrayList < String > ();
L1.addAll(ActualMap.keySet());
for (String x: L1) {
System.out.println(x.toString());
}
You convert Set to List without adding ordering information (like sorting) just to store it in the map.
Because Set is unordered and no ordering information is added, List should not be used, as it will contain randomly ordered data and all it's methods that are related to ordered data will be ambiguous.
You should use Collection interface instead, that accepts both Set and List in the map. This way, no additional memory is required as you use polymorphism instead of copying data.
Map<String, Collection<String>> mainMap = new HashMap<>();
for (int i=0; i < something.size(); i++) {
Set<String> set = getSet(...); //returns different result each time
mainMap.put(differentKeyName, set);
}
Disclaimer: my edit to a similar answer was rejected so I added my own answer with additional information
Map<String, List> mainMap = new HashMap<String, List>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...); //return different result each time
mainMap.put(differentKeyName, new ArrayList(set));
}
I'm writing an adapter framework where I need to convert a list of objects from one class to another. I can iterate through the source list to do this as in
Java: Best way of converting List<Integer> to List<String>
However, I'm wondering if there is a way to do this on the fly when the target list is being iterated, so I don't have to iterate through the list twice.
Java 8 way:
List<String> original = ...;
List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
assuming Wrapper class has a constructor accepting a String.
My answer to that question applies to your case:
import com.google.common.collect.Lists;
import com.google.common.base.Functions
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
List<String> strings = Lists.transform(integers, Functions.toStringFunction());
The transformed list is a view on the original collection, so the transformation happens when the destination List is accessed.
As an alternative to the iterator pattern, you can use a abstract generic mapper class, and only override the transform method:
create a generic collection mapper for any data type
[optional] create a library of methods that transform between different data types (and override the method)
use that library
the implementation:
// Generic class to transform collections
public abstract class CollectionTransformer<E, F> {
abstract F transform(E e);
public List<F> transform(List<E> list) {
List<F> newList = new ArrayList<F>();
for (E e : list) {
newList.add(transform(e));
}
return newList;
}
}
// Method that transform Integer to String
// this override the transform method to specify the transformation
public static List<String> mapIntegerToStringCollection(List<Integer> list) {
CollectionTransformer transformer = new CollectionTransformer<Integer, String>() {
#Override
String transform(Integer e) {
return e.toString();
}
};
return transformer.transform(list);
}
// Example Usage
List<Integer> integers = Arrays.asList(1,2);
List<String> strings = mapIntegerToStringCollection(integers);
This would be useful is you have to use transformations every time, encapsulating the process.
So you can make a library of collection mappers very easy.
If you are trying to get a list of elements within a list then use the below code.Here the list of objects contains attribute name and below gets you list of names from that list
inputList.stream().map(p -> p.getName()).collect(Collectors.toList());
You can write a mapping iterator that decorates an existing iterator and applies a function on it. In this case, the function transforms the objects from one type to another "on-the-fly".
Something like this:
import java.util.*;
abstract class Transformer<T, U> implements Iterable<U>, Iterator<U> {
public abstract U apply(T object);
final Iterator<T> source;
Transformer(Iterable<T> source) { this.source = source.iterator(); }
#Override public boolean hasNext() { return source.hasNext(); }
#Override public U next() { return apply(source.next()); }
#Override public void remove() { source.remove(); }
public Iterator<U> iterator() { return this; }
}
public class TransformingIterator {
public static void main(String args[]) {
List<String> list = Arrays.asList("1", "2", "3");
Iterable<Integer> it = new Transformer<String, Integer>(list) {
#Override public Integer apply(String s) {
return Integer.parseInt(s);
}
};
for (int i : it) {
System.out.println(i);
}
}
}
Lambdaj allows to do that in a very simple and readable way. For example, supposing you have a list of Integer and you want to convert them in the corresponding String representation you could write something like that;
List<Integer> ints = asList(1, 2, 3, 4);
Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
public String convert(Integer i) { return Integer.toString(i); }
});
Lambdaj applies the conversion function only while you're iterating on the result.
There is also a more concise way to use the same feature. The next example works supposing that you have a list of persons with a name property and you want to convert that list in an iterator of person's names.
Iterator<String> namesIterator = convertIterator(persons, on(Person.class).getName());
Pretty easy. Isn't it?
This Could be a solutions --> by using map
List<Employee> employee = Arrays.asList(new Emp(1, 100), new Emp(2, 200), new Emp(3, 300));
List<Employee> employeS = employee.stream()
.map(emp -> new Emp(emp.getId(), emp.getSalary * 100))
.collect(Collectors.toList());
employeS .stream() .forEach(s -> System.out.println("Id :" + s.getId() + " Salary :" + s.getSalary()));
That question does not iterate through the list twice. It just iterates once and by far is the only known method.
Also you could use some transformer classes in commons-collections of google-collections but they all do the same thing under the hood :) the following being one way
CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
Well, you could create your own iterator wrapper class to do this. But I doubt that you would save much by doing this.
Here's a simple example that wraps any iterator to a String iterator, using Object.toString() to do the mapping.
public MyIterator implements Iterator<String> {
private Iterator<? extends Object> it;
public MyIterator(Iterator<? extends Object> it) {
this.it = it;
}
public boolean hasNext() {
return it.hasNext();
}
public String next() {
return it.next().toString();
}
public void remove() {
it.remove();
}
}
I think you would either have to create a custom List (implementing the List interface) or a custom Iterator. For example:
ArrayList<String> targetList = new ArrayList<String>();
ConvertingIterator<String> iterator = new ConvertingIterator<String>(targetList);
// and here you would have to use a custom List implementation as a source List
// using the Iterator created above
But I doubt that this approach would save you much.
Here's an on-the-fly approach. (There must be something already like this in the jdk; I just can't find it.)
package com.gnahraf.util;
import java.util.AbstractList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
*
*/
public class Lists {
private Lists() { }
public static <U,V> List<V> transform(List<U> source, Function<U, V> mapper) {
return new ListView<U, V>(source, mapper);
}
protected static class ListView<U, V> extends AbstractList<V> {
private final List<U> source;
private final Function<U, V> mapper;
protected ListView(List<U> source, Function<U, V> mapper) {
this.source = Objects.requireNonNull(source, "source");
this.mapper = Objects.requireNonNull(mapper, "mapper");
}
#Override
public V get(int index) {
return mapper.apply(source.get(index));
}
#Override
public int size() {
return source.size();
}
}
}
I have data that is organized in kind of a "key-key" format, rather than "key-value". It's like a HashMap, but I will need O(1) lookup in both directions. Is there a name for this type of data structure, and is anything like this included in Java's standard libraries? (or maybe Apache Commons?)
I could write my own class that basically uses two mirrored Maps, but I'd rather not reinvent the wheel (if this already exists but I'm just not searching for the right term).
There is no such class in the Java API. The Apache Commons class you want is going to be one of the implementations of BidiMap.
As a mathematician, I would call this kind of structure a bijection.
In addition to Apache Commons, Guava also has a BiMap.
Here is a simple class I used to get this done (I did not want to have yet another third party dependency). It does not offer all features available in Maps but it is a good start.
public class BidirectionalMap<KeyType, ValueType>{
private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();
synchronized public void put(KeyType key, ValueType value){
keyToValueMap.put(key, value);
valueToKeyMap.put(value, key);
}
synchronized public ValueType removeByKey(KeyType key){
ValueType removedValue = keyToValueMap.remove(key);
valueToKeyMap.remove(removedValue);
return removedValue;
}
synchronized public KeyType removeByValue(ValueType value){
KeyType removedKey = valueToKeyMap.remove(value);
keyToValueMap.remove(removedKey);
return removedKey;
}
public boolean containsKey(KeyType key){
return keyToValueMap.containsKey(key);
}
public boolean containsValue(ValueType value){
return keyToValueMap.containsValue(value);
}
public KeyType getKey(ValueType value){
return valueToKeyMap.get(value);
}
public ValueType get(KeyType key){
return keyToValueMap.get(key);
}
}
If no collisions occur, you can always add both directions to the same HashMap :-)
Here my 2 cents.
Or you can use a simple method with generics. Piece of cake.
public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
Map<V, K> result = new HashMap<V, K>();
for(K k: toInvert.keySet()){
result.put(toInvert.get(k), k);
}
return result;
}
Of course you must have a map with unique values. Otherwise, one of them will be replaced.
Inspired by GETah's answer I decided to write something similar by myself with some improvements:
The class is implementing the Map<K,V>-Interface
The bidirectionality is really guaranteed by taking care of it when changing a value by a put (at least I hope to guarantee it hereby)
Usage is just like a normal map, to get a reverse view on the mapping call getReverseView(). The content is not copied, only a view is returned.
I'm not sure this is totally fool-proof (actually, it's probably not), so feel free to comment if you notice any flaws and I'll update the answer.
public class BidirectionalMap<Key, Value> implements Map<Key, Value> {
private final Map<Key, Value> map;
private final Map<Value, Key> revMap;
public BidirectionalMap() {
this(16, 0.75f);
}
public BidirectionalMap(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public BidirectionalMap(int initialCapacity, float loadFactor) {
this.map = new HashMap<>(initialCapacity, loadFactor);
this.revMap = new HashMap<>(initialCapacity, loadFactor);
}
private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
this.map = map;
this.revMap = reverseMap;
}
#Override
public void clear() {
map.clear();
revMap.clear();
}
#Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return revMap.containsKey(value);
}
#Override
public Set<java.util.Map.Entry<Key, Value>> entrySet() {
return Collections.unmodifiableSet(map.entrySet());
}
#Override
public boolean isEmpty() {
return map.isEmpty();
}
#Override
public Set<Key> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
#Override
public void putAll(Map<? extends Key, ? extends Value> m) {
m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
}
#Override
public int size() {
return map.size();
}
#Override
public Collection<Value> values() {
return Collections.unmodifiableCollection(map.values());
}
#Override
public Value get(Object key) {
return map.get(key);
}
#Override
public Value put(Key key, Value value) {
Value v = remove(key);
getReverseView().remove(value);
map.put(key, value);
revMap.put(value, key);
return v;
}
public Map<Value, Key> getReverseView() {
return new BidirectionalMap<>(revMap, map);
}
#Override
public Value remove(Object key) {
if (containsKey(key)) {
Value v = map.remove(key);
revMap.remove(v);
return v;
} else {
return null;
}
}
}
Quite an old question here, but if someone else has brain block like I just did and stumbles on this, hopefully this will help.
I too was looking for a bi-directional HashMap, sometimes it is the simplest of answers that are the most useful.
If you do not wish to re-invent the wheel and prefer not to add other libraries or projects to your project, how about a simple implementation of parallel arrays (or ArrayLists if your design demands it).
SomeType[] keys1 = new SomeType[NUM_PAIRS];
OtherType[] keys2 = new OtherType[NUM_PAIRS];
As soon as you know the index of 1 of the two keys you can easily request the other. So your lookup methods could looks something like:
SomeType getKey1(OtherType ot);
SomeType getKey1ByIndex(int key2Idx);
OtherType getKey2(SomeType st);
OtherType getKey2ByIndex(int key2Idx);
This is assuming you are using proper object oriented structures, where only methods are modifying these arrays/ArrayLists, it would be very simple to keep them parallel. Even easier for an ArrayList since you would not have to rebuild if the size of the arrays change, so long as you add/remove in tandem.