I have a HashSet<T> of objects that I want to insert into a Map<String,T> where the String key is a property of T. What is most efficient way to do this? I'm guessing there is a better way than what I have currently:
Map<String,T> myMap = new HashMap<String,T>();
HashSet<T> mySet = new LinkedHashSet<T>();
... Add some T's to mySet
for(T t : mySet){
myMap.put(t.getObjectKey(), t);
}
You can achieve that in Java 8 using lambda expressions :
Map<String, T> myMap = mySet.stream()
.collect(Collectors.toMap(x -> x.getObjectKey(),x -> x));
where T is the type of your objects and assuming that x.getObjectKey() returns a String.
Demo here.
The Guava library allows you to use a more functional-style way to express roughly what you're doing, but it's not going to be more efficient, and with the current state of the Java programming language it won't be particularly pretty either.
Until we get lambda expressions in Java 8, you're pretty much stuck doing exactly what you've done.
A bit late to answer, bu i will make an answer: If i required to have options something like this, i would design a custom HashMap for myself following OOP tradition: allowing any type of Collection of type Set and List.
The Object class which are going to be mapped as (key, value) pair entry in HashMap<K, V> should have a function getRelationalKey() with return type K. Hence we need to mark the object class as a type Relational for forcing the implementation. We will use the help of an interface:
interface Relational<T>
{
public T getRelationalKey();
}
The HashMap<K, V> should have a function putAllFromRCollection(Collection) to put all such Object of the collection to the Map. Hence, the function declaration with signature should be something like: putAllFromRCollection(Collection<? extends Relational<K>> collection. Hence, the custom HashMap: MyHashMap can have an implementation like:
class MyHashMap<K, V> extends HashMap<K, V>
{
public void putAllFromRCollection(Collection<? extends Relational<K>> collection)
{
for(Relational<K> relObj: collection)
{
put(relObj.getRelationalKey(), (V)relObj);
}
}
}
It is time for a test case: Lets declare a class:
class ATestClass implements Relational<String>
{
public String name;
public String address;
public ATestClass(String name, String address) {
this.name = name;
this.address = address;
}
#Override
public String getRelationalKey() {
return name;// or address or whatever should be regarded as a key
}
}
And add some object of this class to a HashSet:
HashSet<ATestClass>testSet = new HashSet<>();
testSet.add(new ATestClass("AName", "A-Adress"));
testSet.add(new ATestClass("BName", "B-Adress"));
testSet.add(new ATestClass("CName", "C-Adress"));
testSet.add(new ATestClass("DName", "D-Adress"));
MyHashMap<String, ATestClass>myMap = new MyHashMap<>();
myMap.putAllFromRCollection(testSet);
System.out.println(myMap);
Output:
{
CName=CollectionsTest.ATestClass#c21495,
BName=CollectionsTest.ATestClass#14b7453,
DName=CollectionsTest.ATestClass#1d5550d,
AName=CollectionsTest.ATestClass#1186fab
}
The advantages are:
we are now allowed to define required data type of the key to make the relation with the object
set a specific target field of Class as the Key matching type of the HashMap'S key
Allowing any type of Collection<E> instead of just a HashSet
In this implementation however, i have avoided to provide code for resizing the map with the size of the provided Collection for performance achievement. Please refer to the putAll(Map) function's source for details.
Related
I have the below method, in which I am extracting the value from the entity and then setting it in map as a value of that map but my point is that for each key I am setting the value explicitly so if the count of keys grows that method code will also grow , can I make a common method based on approach Map.computeIfPresent, please advise how can I achieve both the things
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put("XXNAME", abcLoginDTO.getUsername());
getMap.put("XX_ID", abcLoginDTO.getClientId());
getMap.put("RR_ID", abcLoginDTO.getUserId());
getMap.put("QQ_TIME", abcuserLoginDTO.getLocktime());
}
something like in this below approach I am thinking
static <E> void setIfPresent(Map<String, Object> map, String key, Consumer<E> setter, Function<Object, E> mapper) {
Object value = map.get(key);
if (value != null) {
setter.accept(mapper.apply(value));
}
}
but my point is that for each key I am setting the value explicitly so
if the count of keys grows that method code will also grow
You need to populate the Map with different values from the DTO, so you don't have other choices.
The method is long because you don't have a mapping between the key to add in the Map and the value to retrieve from the DTO.
You could write your code with a function such as :
static void setValueInMap(Map<String, Object> map, String key, Supplier<Object> mapper) {
map.put(key, mapper.get());
}
And use that :
Map<String, Object> map = ...;
AbcLoginDTO dto = ...;
setIfPresent(map, "keyUserName", dto::getUserName);
// and so for
But no real advantages.
Your second snippet has not at all relationship with the first one.
If i understand correctly, what you want to do is iterate over all of the object's members, get their value, and set them to a map according to their name. If so, then what you're looking for is called Reflection.
Every object can give you an array of its fields or methods (even private ones!) and then you can manipulate them using the Field / Method object.
Field[] members = AbcLoginDTO.class.getDeclaredFields();
Map<String, Object> values = new HashMap<>();
for(Field member : members) {
member.setAccessible(true);
values.put(member.getName(), member.get(abcLoginDTO));
}
What you end up with here, is a "Map representation" of your AbcLoginDTO instance. from here you can do with it what you want...
notice that i am "inspecting" the class itself in line 1, and then using the instance at line 6.
this code is not complete, but it's a start, and this can also be adapted to work for ANY object.
I don't know if I understood correctly, but if I did then that means all you need is a way to manually set different keys for the methods of your AbcLoginDTO class
If so then that can be done easily,
let's consider that your abcLoginDTO.getClientId() is always different for every AbcLoginDTO object:
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_NAME", abcLoginDTO.getUsername());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getClientId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getUserId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_TIME", abcuserLoginDTO.getLocktime());
}
I'm kind of new to generics in Java and I've faced such a problem: Let's say you have your own List implementation and you want to provide a mechanism to simultaneously convert all elements using some kind of mapping (functional interface) and collect them into a new list.
While the idea and use of functional interface (IMapper in my case) is straightforwad I can't quite think of what signature a function performing mapping should have?
Here's a little use case example and what I thought of as well. It does not work unfortunately and I guess the main problem is: How the second V param type should be passed in such case?
public interface IMapper<T,V> { V map(T v); }
public class MyList<T> extends ArrayList<T> {
public MyList<V> map(IMapper <T,V> mapper) {
MyList<V> list = new MyList<>();
for(T v : this) {
list.add(mapper.map(v));
}
return list;
}
}
// in main
MyList<Integer> list1 = new MyList<>();
// fill etc..
IMapper<Integer,String> m = (i) -> { return i.toString(); };
// "Change" list
MyList<String> list2 = list1.map(m);
PS:
I think that such thing is most probably already implemented in Java (stream() and what follows I guess?) however it suppose to be exercise for me. Any tip would be much appreciated :)
You can add the map result type to you function definition as following:
class MyList<T> extends ArrayList<T> {
public <V> MyList<V> map(IMapper<T, V> mapper) {
MyList<V> list = new MyList<>();
for (T v : this) {
list.add(mapper.map(v));
}
return list;
}
}
Example:
MyList<Integer> number = new MyList<>();
number.add(1);
number.add(2);
number.add(3);
number.map(v -> "#" + v).forEach(System.out::println);
And you can have the same result using Java8 streams as following:
List<Integer> numberStream = new ArrayList<>();
numberStream.add(1);
numberStream.add(2);
numberStream.add(3);
numberStream.stream().map(v -> "#" + v).forEach(System.out::println);
You can define the generic parameter as a type parameter on your map method. Like this:
public <V> MyList<V> map(IMapper <T,V> mapper) {
...
}
Type parameters can be defined in two ways, either on a class or on a method. If it's defined on a class, it can be used throughout the class. If it's defined on a method, it can only be used in that method.
In your case, the T parameter is defined on the class, while the V parameter can be defined on the method.
I am working on java and currently trying to search a list of object of a particular DummyUserList type.
public class DummyUserList implements Serializable {
String firstName;
String lastName;
String city;
----
----
setter andd getter will go here.
}
and
public static void main(String[] args) {
List<DummyUserList> searchListObj = new ArrayList<DummyUserList>();
searchListObj.add(dummyUserList1);
searchListObj.add(dummyUserList2);
searchListObj.add(dummyUserList3);
String toBeSearched = "Singhaniya";
List<DummyUserList> result = searchListObj.stream()
.filter(a -> Objects.equals(a.lastName, toBeSearched))
.collect(Collectors.toList());
System.out.println("result--->" + result);
}
Currently, the above code is searching DummyUserList object on the basis of lastName. It's working fine, but how can I pass a generic collection object to the filter method for searching?
I am trying in the following way: I am passing a generic collection object and it's supposed to be search and return a list of searched object, but I'm getting a syntax error saying "searchText can not be resolved or is not a field".I want to do something like below code.
public static <T> List<T> search(Collection<T> collectionObject,
String searchProperty, String searchText) {
List<T> result = collectionObject.stream()
.filter(a -> Objects.equals(((List<T>) collectionObject).searchText))
.collect(Collectors.toList());
}
How do I pass a generic collection to the above method for searching? Is it possible to search a dynamic collection object?
If you search a Collection<T>, each element of the collection is of type T. Not of type List<T>. So your cast doesn't make sense.
Objects.equals() takes two arguments, not one.
And you can't just pass the name of a property as a String (you would need reflection to access the field). Instead, you need a way to transform each T object into one of its properties of type String, in order to compare this property with the search text. So you need a Function<T, String>:
public static <T> List<T> search(Collection<T> collectionObject,
Function<T, String> searchPropertyAccessor, String searchText) {
List<T> result = collectionObject.stream()
.filter(item -> Objects.equals(searchPropertyAccessor.apply(item), searchText))
.collect(Collectors.toList());
}
And you would call it this way:
List<User> foundUsers = search(users, User::getLastName, "Doe");
Note that I chose to rename your DummyUserList class to User, since what this class represents is a user, not a user list. Also note that I used a getter to access its last name, as public fields are a bad idea in general, and prevent the use of method references, which are quite handy and expressive.
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 8 years ago.
I saw this thread sorting a List of Map<String, String> and I know mine could sound a duplicate, but it is slight differen.
My example is:
List<Map<String, String>> myList = new ArrayList<Map<String, String>>();
...
for(MyClass1 c1 : c1)
{
...
for(MyClass2 c2 : c12)
{
SimpleBindings myBindindings= new SimpleBindings();
myBindindings.put(c1.getName(), c2.getName());
myList.add(myBindindings);
}
}
...
Concretely I can have
{
(John, Mike)
(John, Jack)
(Sam, Jack)
(Gloria, Anna)
(Jane, Carla)
...
}
and would like that my list is sorted by the maps key:
{
(Gloria, Anna)
(Jane, Carla)
(John, Mike)
(John, Jack)
(Sam, Jack)
...
}
Are you sure that
List<Map<String, String>>
is the approriate data type you want?
To me it looks like you are in fact looking simplify for
TreeMap<String, String>
i.e. a sorted map key -> value?
Or do you mean to use a List<StringPair> (for that, please choose a more appropriate name than StringPair, and implement that class to your needs)? I have the impression that in lack of an obvious Pair<String, String> class in Java you have been abusing SimpleBinding as a pair class. The proper way to have pairs in Java is to implement a new class, with a proper class name - "pair" is technical, not semantic.
You could also do
List<String[]>
and implement a Comparator<String[]> for sorting. But that doesn't save you any work over implementing a NamePair class and making it comparable yourself.
You need to implement Comparator to accomplish this...
Collections.sort(myList, new Comparator<ObjectBeingCompared>() {
#Override
public int compare(ObjectBeingCompared obj1, ObjectBeingCompared obj2) {
//Of course you will want to return 1, 0, -1 based on whatever you like
//this is just a simple example
//return 1 if obj1 should be ordered first
//return 0 if obj1 and obj2 are the same
//return -1 if obj1 should be ordered after obj2
return obj1.compareTo(obj2);
}
});
The HashMap data structure is used to allow access to its elements in O(1) time.
Because it is a container of data its pool or keys can vary in time. This mean that you can not assure in long therm an order for list of maps.
In your example you match two Strings and create Pair of data called SimpleBindings.
In case of your simple example you should not use Map<String,String> data structure to represent a Pair of data.
If you SimpleBindings really consist of two string, everything you must do is only implement a Comparable in SimpleBindings class like this:
class SimpleBinding implements Comparable<SimpleBinding> {
private final String key;
private final String value;
public SimpleBinding(String key, String value) {
Objects.nonNull(key);
Objects.nonNull(value);
this.key = key;
this.value = value;
}
#Override
public int compareTo(SimpleBinding that) {
return this.key.compareTo(that.key);
}
}
And the you just use the Collections.sort(bindings ) to have sorted result.
In case you do not have access to the class you should use the Comparator interface like this
enum SimpleBindingComparator implements Comparator<SimpleBinding> {
DEFUALT {
#Override
public int compare(SimpleBinding fist, SimpleBinding second) {
return fist.key.compareTo(second.key);
}
};
Then you sort your bindings like this Collections.sort(bindings ,SimpleBindingComparator.DEFAULT);
But if your case is more complex than this and your store a Map in the list you should define a logic that represent the order. In your case it can be sad that the order must maintained by c1.getName()
One choice is that you should not create a List but a map of list Map<String>,List<String>> this is so called multi map where a single key matches to multiple values. See MultiMap of guava and if you want it to be sorted then i propose to read about TreeMultiMap
What kind of collection should I use if I need to create a collection that will allow me to store books and how many copies there are in circulation (for a library)?
I would use an ArrayList, but I also want to be able to sort the books by order of issue year.
You can create a Book Class with all the attributes you have for a book. And implement a Comparable for that Book Class and write sorting logic in there.
Maintain a List<Book>, and use Collections.sort method, to sort your List according to the implemented Sorting logic.
UPDATE: -
As far as, fast look-up is concerned, a Map is always the best bet. And is appropriate to implement a dictionary look-up kind of structure. For that, you would need some attribute that uniquely identifies each book. And then store your book as Map<String, Book>, where your key might be id of type String.
Also, in this case, your sorting logic will change a little. Now you would have to sort on the basis of your Map's value, i.e. on the basis of attributes of Book.
Here's a sample code you can make use of. I have just considered sorting on the basis of id. You can change the sorting logic as needed: -
class Book {
private int id;
private String title;
public Book() {
}
public Book(int id, String title) {
this.id = id;
this.title = title;
}
#Override
public String toString() {
return "Book[Title:" + this.getTitle() + ", Id:" + this.getId() + "]";
}
// Getters and Setters
}
public class Demo {
public static void main(String[] args) {
final Map<String, Book> map = new HashMap<String, Book>() {
{
put("b1", new Book(3, "abc"));
put("b2", new Book(2, "c"));
}
};
List<Map.Entry<String, Book>> keyList = new LinkedList<Map.Entry<String, Book>>(map.entrySet());
Collections.sort(keyList, new Comparator<Map.Entry<String, Book>>() {
#Override
public int compare(Map.Entry<String, Book> o1, Map.Entry<String, Book> o2) {
return o1.getValue().getId() - o2.getValue().getId();
}
});
Map<String, Book> result = new LinkedHashMap<String, Book>();
for (Iterator<Map.Entry<String, Book>> it = keyList.iterator(); it.hasNext();) {
Map.Entry<String, Book> entry = it.next();
result.put(entry.getKey(), entry.getValue());
}
System.out.println(result);
}
}
OUTPUT: -
"{b2=Book[Title:c, Id:2], b1=Book[Title:abc, Id:3]}"
Well, If the entire purpose of your collection is to store the counts of the books, than a dictionary/map, or whatever java's key-value collection is called.
It would probably have title as your key, and the count as your value.
Now I suspect that your collection might be a little more complicated than that, so you might want to make a Book class which has Count as a field, and then I'd probably have a string -> Book dictionary/map anyway, with the string as it's dewy decimal number or some other unique identifier.
Beyond a simple educational or toy project, you'd want to use a database rather than an in-memory collection. (Not really an answer, but I think worth stating.)
java.util.TreeMap can be used to index and sort this kind of requirements.
Check http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html for more details.
You can use your Book object as key mapped to the number of copies as the value.