Creating instance list of different objects - java

I'm tring to create an arraylist of different class instances. How can I create a list without defining a class type? (<Employee>)
List<Employee> employees = new ArrayList<Employee>();
employees.add(new Employee());
Employee employee = employees.get(0);

You could create a list of Object like List<Object> list = new ArrayList<Object>(). As all classes implementation extends implicit or explicit from java.lang.Object class, this list can hold any object, including instances of Employee, Integer, String etc.
When you retrieve an element from this list, you will be retrieving an Object and no longer an Employee, meaning you need to perform a explicit cast in this case as follows:
List<Object> list = new ArrayList<Object>();
list.add("String");
list.add(Integer.valueOf(1));
list.add(new Employee());
Object retrievedObject = list.get(2);
Employee employee = (Employee)list.get(2); // explicit cast

List<Object> objects = new ArrayList<Object>();
objects list will accept any of the Object
You could design like as follows
public class BaseEmployee{/* stuffs */}
public class RegularEmployee extends BaseEmployee{/* stuffs */}
public class Contractors extends BaseEmployee{/* stuffs */}
and in list
List<? extends BaseEmployee> employeeList = new ArrayList<? extends BaseEmployee>();

List anyObject = new ArrayList();
or
List<Object> anyObject = new ArrayList<Object>();
now anyObject can hold objects of any type.
use instanceof to know what kind of object it is.

I believe your best shot is to declare the list as a list of objects:
List<Object> anything = new ArrayList<Object>();
Then you can put whatever you want in it, like:
anything.add(new Employee(..))
Evidently, you will not be able to read anything out of the list without a proper casting:
Employee mike = (Employee) anything.get(0);
I would discourage the use of raw types like:
List anything = new ArrayList()
Since the whole purpose of generics is precisely to avoid them, in the future Java may no longer suport raw types, the raw types are considered legacy and once you use a raw type you are not allowed to use generics at all in a given reference. For instance, take a look a this another question: Combining Raw Types and Generic Methods

How can I create a list without defining a class type? (<Employee>)
If I'm reading this correctly, you just want to avoid having to specify the type, correct?
In Java 7, you can do
List<Employee> list = new ArrayList<>();
but any of the other alternatives being discussed are just going to sacrifice type safety.

If you can't be more specific than Object with your instances, then use:
List<Object> employees = new ArrayList<Object>();
Otherwise be as specific as you can:
List<? extends SpecificType> employees = new ArrayList<? extends SpecificType>();

I see that all of the answers suggest using a list filled with Object classes and then explicitly casting the desired class, and I personally don't like that kind of approach.
What works better for me is to create an interface which contains methods for retrieving or storing data from/to certain classes I want to put in a list. Have those classes implement that new interface, add the methods from the interface into them and then you can fill the list with interface objects - List<NewInterface> newInterfaceList = new ArrayList<>() thus being able to extract the desired data from the objects in a list without having the need to explicitly cast anything.
You can also put a comparator in the interface if you need to sort the list.

I know this is an old question, but there's a nice and easy way to do this (it works with the mostly recent versions of ElasticSearch Rest API).
The search object goes like:
SearchResponse<JsonData> search = client.search(s -> s
.index(index)
.query(query),
JsonData.class);
And then I iterate over the response like this:
for (Hit<JsonData> hit: search.hits().hits()) {
String stringSource = hit.source().toString();
MySavedRegister mySavedRegister = mapper.readValue(stringSource, mySavedRegister .class);
mylist.add(esGenericEvent);
}
Where mySavedRegister stands for the class that has the hits' sources parameters.

Related

Unable to Cast Map into Java Class

public class Model extends LinkedHashMap<String, Object>{
}
LinkedHashMap<String, Object> map = //instance of LinkedHashMap
Model model = (Model) map // Exception.
when I'm trying to cast LinkedHashMap<String,Object> into my Model class I'm getting class cast exception.
I'm basically using user defined method which expect Model as an argument that's why I'm casting my map into Model class
You cannot do that because Model is not an instanceof LinkedHashMap. Think in terms of memory allocation. What have you actually done? You have allocated enough memory for a LinkedHashMap but NOT for a Model. The compiler is trying to save you from yourself.
You cannot use LinkedHashMap where Model is expected, therefore you cannot cast.
Presumably Model has additional methods and more functionality than LinkedHashMap. Even if you did not add anything new in Model class yet, Java assumes that you will.
Possible solution: change
LinkedHashMap<String, Object> map = //instance of LinkedHashMap
to
LinkedHashMap<String, Object> map = //instance of Model
Another solution: change the code that uses Model class to use Map instead.
Still another approach: do not derive Model from Map, have Model own a Map. Change the code accordingly.
In general, you need to learn more about OO programming, especially the modern sort that deprecates inheritance in favor of aggregation.
API Specifications for the ClassCastException says:
Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance.
class P {
}
class C extends P {
}
P p = new P();
C c = (C) p;//java.lang.ClassCastException; Runtime Exception
If you really want to do it. You do it in this way:
LinkedHashMap map = new Model();
map.put("one", "1");
Model model = (Model) map; // Exception.
System.out.println(model);
referece: Explanation of "ClassCastException" in Java

Collection sort of list contains 2 objects

I have a list, which contains 2 objects of type Departments and Managers.
I need to sort the list alphabetically by Departments. The Department class implements Comparable<Department> and has the method compareTo. But I receive an error message:
"The method sort(List, Comparator) in the type
Collections is not applicable for the arguments (List, new
Comparator(){})"
public static List<?> getLst(String fileName)throws FileNotFoundException, IOException,ClassNotFoundException {
List<Object> lst = new LinkedList<>();
BufferedInputStream f;
try(ObjectInputStream i = new ObjectInputStream(f=new BufferedInputStream(new FileInputStream(fileName)))){
while (f.available()>0) {
lst.add(i.readObject());
}
Collections.sort(lst, new Comparator<Department>() {
#Override
public int compare(Department object1,Department object2) {
return object1.getDepName().compareTo(object2.getDepName());
}
});
}
return lst;
}
You are not using generics correctly.
Collections.sort() wants:
a List<T> and
a matching Comparator<T>
You are providing a List of Objects; but a Comparator of Departments. That simply can't work.
So, one way of resolving this - first change your list to use the proper generic type:
List<Department> departments = new ArrayList<>();
and later ... add the (now necessary) cast so that the incoming "Object" can be added to that list of departments - if it has the correct type!
Object fromStream = i.readObject();
if (fromStream instanceof Department) {
departments.add( (Department) fromStream);
} else {
// consider what to do with managers objects ...
( please note: lst is a bad name; it says nothing, and that one saved character only adds confusion. Use names that express what the thing behind is about; like departments to make clear: something that contains, well departments in plural )
Update: the generics give a hint that you have a design problem here. If you want to sort departments, then your list containing departments should not contain anything else.
In other words: the real answer here is to not use one list that contains different types of objects, but to use two lists instead.
The design that puts managers an departments in the same list sounds suspect. Are you making a model for a Tree widget? There are better ways for that ...
In any case, you can do one of two things:
provide Comaparator and cast inside as needed (bad idea)
or have both Department and Manager implement HasDepartment with getDepartment() method, and build your Comparator for that.

convert List of hashmap into set

i want to cast List<HashMap<String, Object>> into Set<StudentInfo>
i have method
public List<HashMap<String,Object>> getStudentData(studentId);
i want to convert the result into Set so i used
Set<StudentInfo> studentFilteredInfo = new HashSet<>();
List<Map<String, Object>> studentCompleteRecord = getStudentData(1005);
studentFilteredInfo.addAll((Collection<? extends StudentInfo>studentCompleteRecord ));
initially when i executed on localhost it with java 8, eclipse and tomcat 8 it is working fine.
when i tried to build it with maven
mvn clean package
it will through an Error:
incompatible types: java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
cannot be converted to java.util.Collection<? extends com.school.model.StudentInfo>
You are mistaken: there is no casting from List<Map<String, Object>> into some Set<Whatever>!
Casting basically means: you know that some "Object" has a more specific type; thus you tell the compiler: "you can safely assume that this thingy here is something else in reality".
But that means: in reality (at runtime), that "thingy" really is "something else"! And alone the generic types that you provide in your question make it very clear: you can't be doing a cast here!
In other words: you have to write code that iterates your List of Maps; to extract that information that is required to create new StudentInfo objects. Then you collect those newly created objects; and put them into a new Set; which you then can return from your method!
And finally: always avoid "concrete" implementation types; you used List<HashMap<... - instead, you should go for List<Map<... !
You need to write code to explicitly convert a Map<String,Object> to a StudentInfo instance. Suppose StudentInfo has a method like this:
static StudentInfo create(Map<String, Object> info) {
String name = info.get("name");
Transcript transcript = info.get("grades");
return new StudentInfo(name, transcript);
}
Then you would need to iterate over each element in the list and use your method to convert the Map instances to StudentInfo objects.
With lambdas:
Set<StudentInfo> studentFilteredInfo = studentCompleteRecord.stream()
.map(StudentInfo::create)
.collect(Collectors.toSet());
Without lambdas:
Set<StudentInfo> studentFilteredInfo = new HashSet<>();
for (Map<String,Object> e : studentCompleteRecord)
studentFilteredInfo.add(StudentInfo.create(e);

How to access an objects set and get methods when it is in an ArrayList?

So I have this code here:
List<XMLMessage> xmlMessageList = new ArrayList<XMLMessage>();
now XMLMessage has set and get methods in here such as setFileContent, setFileName, and setFileDirectory and these are all Strings.
So how do I access those setmethods?
I am guessing it's something like this?
List xmlMessageList = new ArrayList();
xmlMessageList.add(setFileContent);
You can access a specific XMLMessage from the list by using the get get(int) method. You can then call methods on this instance. E.g.:
List<XMLMessage> xmlMessageList = new ArrayList<>();
xmlMessageList.add(new XMLMessage());
XMLMessageList.get(0).setFileName("my_file.txt");

Conditional mapping to new objects with a Java Stream

I have a stream of objects (a List) and want to create new objects from that stream, to be inserted into a Set. However, two or more objects in the incoming List may hash to the same Key in the Set, in which case I want to append a String from the nth List object to the one already in the Set instead of creating a new one.
Something like this, but in functional form:
HashSet<ClassB> mySet = new HashSet<>();
for (ClassA instanceA : classAList) {
if (mySet.contains(ClassB.key(instanceA))) { //static method call to find the key
mySet.get(instanceA).appendFieldA(instanceA.getFieldA());
} else {
mySet.add(new ClassB(instanceA));
}
}
return mySet;
In functional form I though of creating something like this:
List classAList = new ArrayList<>();
classAList.stream()
.map(instanceA -> new ClassB(instanceA))
.collect(Collectors.toSet());
But then of course that ignores the hashmap and I don't get to combine fields my multiple instances of ClassA that would all resolve to the same ClassB. I'm not sure how to put that in there. Do I need ignore the map() call and create a custom collector instead to do this job? There seems to be more than one way to do this, but I'm new to Streams.
It’s hard to understand what you actually want as your code example does not work at all. The problem is that a Set does not work like a Map, you can’t ask it for the contained equivalent object. Besides that, you are using different objects for your contains(…) and get(…) call. Also, it’s not clear what the difference between ClassB.key(instanceA) and new ClassB(instanceA) is.
Let’s try to redefine it:
Suppose we have a key type Key and a method Key.key(instanceA) to define the group candidates. Then we have a ClassB which is the resulting type, created via new ClassB(instanceA) for a single (or primary ClassA instance), having an .appendFieldA(…) method to receive a value of another ClassA instance when merging two group members. Then, the original (pre Java 8) code will look as follows:
HashMap<Key, ClassB> myMap = new HashMap<>();
for(ClassA instanceA: classAList) {
Key key=Key.key(instanceA);
if(myMap.containsKey(key)) {
myMap.get(key).appendFieldA(instanceA.getFieldA());
} else {
myMap.put(key, new ClassB(instanceA));
}
}
Then, myMap.values() provides you a collection of the ClassB instances. If it has to be a Set, you may create it via
Set<ClassB> result=new HashSet<>(myMap.values());
Note that this also works, when Key and ClassB are identical as it seems to be in your code, but you may ask youself, whether you really need both, the instance created via .key(instanceA) and the one created via new ClassB(instanceA)…
This can be simplified via the Java 8 API as:
for(ClassA instanceA: classAList) {
myMap.compute(Key.key(instanceA), (k,b)-> {
if(b==null) b=new ClassB(instanceA);
else b.appendFieldA(instanceA.getFieldA());
return b;
});
}
or, if you want it look even more function-stylish:
classAList.forEach(instanceA ->
myMap.compute(Key.key(instanceA), (k,b)-> {
if(b==null) b=new ClassB(instanceA);
else b.appendFieldA(instanceA.getFieldA());
return b;
})
);
For a stream solution, there is the problem, that a merge function will get two instances of the same type, here ClassB, and can’t access the ClassA instance via the surrounding context like we did with the compute solution above. For a stream solution, we need a method in ClassB which returns that first ClassA instance, which we passed to its constructor, say getFirstInstanceA(). Then we can use:
Map<Key, ClassB> myMap = classAList.stream()
.collect(Collectors.toMap(Key::key, ClassB::new, (b1,b2)->{
b1.appendFieldA(b2.getFirstInstanceA().getFieldA());
return b1;
}));
You can group the entries into a map that maps the hashed key to the list of elements and then call map again to convert that map into the set you are after. Something like this:
List classAList = new ArrayList<>();
classAList.stream()
.collect(Collectors.groupingBy(instanceA -> ClassB.key(instanceB)))
.entrySet()
.map(entry -> entry.getValue().stream()
.map(instanceA -> new ClassB(instanceA))
.reduce(null, (a,b) -> a.appendFieldA(b)))
.collect(Collectors.toSet());

Categories