I'm new to Java so I'm probably doing something wrong here,
I want to create an array of Sets and I get an error (from Eclipse).
I have a class:
public class Recipient
{
String name;
String phoneNumber;
public Recipient(String nameToSet, String phoneNumberToSet)
{
name = nameToSet;
phoneNumber = phoneNumberToSet;
}
void setName(String nameToSet)
{
name = nameToSet;
}
void setPhoneNumber(String phoneNumberToSet)
{
phoneNumber = phoneNumberToSet;
}
String getName()
{
return name;
}
String getPhoneNumber()
{
return phoneNumber;
}
}
and I'm trying to create an array:
Set<Recipient>[] groupMembers = new TreeSet<Recipient>[100];
The error I get is "Cannot create a generic array of TreeSet"
What is wrong ?
From http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html:
you cannot instantiate an array of a generic type (new List<String>[3] is illegal), unless the type argument is an unbounded wildcard (new List<?>[3] is legal).
Rather than using an array, you can use an ArrayList:
List<Set<Recipient>> groupMembers = new ArrayList<Set<Recipient>>();
The code above creates an empty ArrayList of Set<Recipient> objects. You would still have to instantiate every Set<Recipient> object that you put into the ArrayList.
Arrays don't support Generics. Use an ArrayList:
ArrayList<Set<Recipient>> groupMembers = new ArrayList<Set<Recipient>>();
You might want to consider using Guava's Multimap where the key is the index. This will handle creating the Sets for each index as you need them.
SetMultimap
SetMultimap<Integer, Recipient> groupMembers;
Related
I need to transform an array of one type to an array of another type.
More specifically, I need to pull just a couple fields from each object in the starting array to create the resulting array, which will contain only those 2 fields, though named differently.
For example, let's say I have an array of Thing objects:
public class Thing {
private String id;
private String description;
... // other fields
}
I need to create from that an array of Item objects:
public class Item {
private String code;
private String data;
...
}
... where the id from each Thing becomes code in each Item; and description becomes data.
I've seen examples of using the Stream api to transform an array of objects to an array of Strings. But it's unclear to me thus far how to transform an object to another object.
Try this.
record Thing(String id, String description) {}
record Item(String coded, String data) {}
public static void main(String[] args) {
Thing[] array = {new Thing("1", "one"), new Thing("2", "two")};
Item[] transformed = Arrays.stream(array)
.map(thing -> new Item(thing.id(), thing.description()))
.toArray(Item[]::new);
System.out.println(Arrays.toString(transformed));
}
output:
[Item[coded=1, data=one], Item[coded=2, data=two]]
I have to do a little exercise (homework, like a friendlist) in Java, and i'm a little stuck on one of the tasks that i have to implement in my program.
The exercise is about storing some friend-objects with a variety of attributes in a container-class and implementing some methods in the container-class for various tasks on the friend-objects.
The overall exercise is not a problem at all, but i'm quite unconvinced that my solution is the way to go. I hope you can give me some tips here.
The method that is left over, should be something like a "updateFriend" method, with which you can set the value of a given attribute to a new value, straight from the container-class.
I've already set up my friend-class with a handfull of attributes (e.g. prename, lastname, date of birth, adress, and so on) an getters/setters for all of them. I've also implemented the container-class (as an ArrayList), but i can't seem to find an elegant way to implement this specific method. My updateFriend()-method right now takes three parameters.
1.The specific id of the friend-object
2.The name of the attribute that i want to change
3.The new value of the attribute
It uses an enum to check if the entered attribute is an existing attribute and if yes, the method searches the ArrayList for the object that contains that attribute and should overwrite the existing value. It gets a little bulky, as i have implemented a switch on the enum, that calls the fitting setter-method for each attribute of the friend, if the type in attribute exists at all.
So basically the friend-class looks like this:
public class Friend {
private static int friendCount = 1;
private String firstname;
private String lastname;
private LocalDate dateOfBirth;
private String phonenumber;
private String mobilenumber;
private String eMail;
private Adress home;
private int friendID;
//Getters & Setters
...
}
The method that gives me problems in the container-class looks something like this at the moment:
public void updateFriend(int id, String toChange, String newValue)
{
for(Attribute a : attribute.values())
{
if(String.valueOf(a).equalsIgnoreCase(toChange))
{
for(Friend f : friends)
{
int counter = 1;
if(f.getID() == id)
{
switch(a)
{
case FIRSTNAME:
{
f.setPreName(neuerWert);
break;
}
//a case for each attribute
}
I'm quite certain that my take on the given method is messy, slow, and cumbersome. What would be an elegant way of solving this?
Excuse my wording and thanks in advance, greets.
I would suggest 3 performance improvements.
Use HashMap instead of List with key as id. Since, id will be unique, it will take O(1) time to get the relevant object for modification instead of spending O(n) time on List iteration.
You can change the type of toChange parameter from String to enum. This will avoid enum to String conversion and then comparing it.
Since, you are already doing validation of the attribute to be modified and you must be following standard java convention while naming your getters and setters, you can use reflection to call the method on the Friend object by creating the method name from attribute name like set{Attributename}.
Okay, lets start using the enum Attribute to handle all the changes (Since you already holding the attribute values)
Attribute Enum
public enum Attribute {
FIRSTNAME("fname", (friend, name) -> friend.setFirstname(String.valueOf(name))),
LASTNAME("lname", (friend, lname) -> friend.setLastname(String.valueOf(lname))),
DATEOFBIRTH("dob", (friend, dob) -> friend.setDateOfBirth((LocalDate) dob)),
PHONENUMBER("pno", (friend, pno) -> friend.setFirstname(String.valueOf(pno))),
MOBILENUMBER("mno", (friend, mno) -> friend.setFirstname(String.valueOf(mno)));
private String attributeName;
private BiConsumer<Friend, Object> attributeSetter;
public static Attribute getAttributeSetterByName(String attributeName) {
return Arrays.stream(Attribute.values())
.filter(attribute -> attribute.getAttributeName().equalsIgnoreCase(attributeName))
.findFirst()
.orElseThrow(() -> new RuntimeException(String.format("Invalid Attribute name - %s", attributeName)));
//.orElse(null);
}
//Getter, Setter & Args Constructor (Use Lombok to reduce Boiler Plate code)
}
Update Logic
public void updateFriend(int id, String toChange, String newValue) {
Attribute attribute = Attribute.getAttributeSetterByName(toChange);
for (Friend friend : friends) {
if (friend.getId() == id) {
attribute.getAttributeSetter().accept(friend, newValue);
break;
}
}
}
You can use a java.util.function.Consumer<T> object to change an object inside your container where you have all the type safety you get. Instead of having magic strings and string arguments for values, which might not be even for string fields, you can work directly on the objects type:
public void updateFriend(int id, Consumer<Friend> c) {
// find the friend object
Friend found = null;
for (Friend f: this.friends) {
if (f.getId() == id) {
found = f;
break;
}
}
if (found == null) {
throw new IllegalArgumentException("There is no friend object with the given id");
}
// use the friend object.
c.accept(found);
}
You can use this method like this:
container.updateFriend(42, f -> f.setVorName("abc"));
container.updateFriend(9, f -> f.setAddress(some_address_object));
I have a list from some complex type and I want to figure a neat way to construct a list only from one of its fields using Java 8's streams. Let's take as an example:
public static class Test {
public Test(String name) {
this.name = name;
}
public String getName() {
return name;
}
private String name;
// other fields
}
And imagine that I have a List<Test> l;. Now I want to create a new list that contains the values of name of all elements in l. One possible solution that I found is the following:
List<String> names = l.stream().map(u ->u.getName()).
collect(Collectors.<String> toList());
But I was wondering if there is a better way to do this - map a list of a given type to another list of different type.
Using method references is shorter :
List<String> names = l.stream().map(Test::getName).
collect(Collectors.toList());
You can't avoid at least two Stream methods, since you must first convert each Test instance to a String instance (using map()) and then you must run some terminal operation on the Stream in order to process the Stream pipeline (in your case you chose to collect the Stream of Strings into a List).
I've been playing around with arrays for some time and this problem has been troubling me.
I created a user defined object and declared it in an array like this: `Property regesteredAssets[] = new Property[200];
And here's my constructor: `
public Property(String newPropertyName,String newPropertyAddress,String newPropertyType, String newPropertyDescription)
{
propertyName[arraySequence] = newPropertyName;
propertyFullAddress[arraySequence] = newPropertyAddress;
propertyType[arraySequence] = newPropertyType;
propertyDescription[arraySequence] = newPropertyDescription;
arraySequence++;
}
I want to initialize each array regesteredAsssets[] according to my desire. How can I do it?
Do I have to use arrays in my attributes in the Property class too?
You do not need your attributes to be arrays, unless a particular asset has multiple of something. In this case, I don't think it does. You can greatly simplify your code as follows:
public class Property {
private String name, address, type, description;
public Property(String name, String address, String type, String description) {
this.name = name;
this.address = address;
this.type = type;
this.description = description;
}
public static void main(String[] args) {
Property[] registeredAssets = new Property[200];
registeredAssets[0] = new Property("Joe Bloggs", "555 Fake St.", "IMPORTANT", "Lorem Ipsum Dolor");
// etc.
}
}
If you have an array of type Property, you can set each of the elements up using the following code:
regesteredAssets[0] = new Property( enterYourParametersHere );
I assume the fields in your Property constructor are single fields, and therefore you do not need to set them using the array notation field[index] = value, and indeed, if the Property class is of the consistency I think it is, then this will produce a compilation error.
If you wanted to set up multiple entries in your array, you could perform the initialisation step inside a loop, providing a loop index to the index of the array as below:
for( int i = 0; i < 10; i++ )
{
regesteredAssets[i] = new Property( enterYourParametersHere );
}
I hope this helps...
I have a Java class as
class Students{
private String fName;
private String lName;
private String uName;
public Students(String fName, String lName, String uName) {
this.fName = fName;
this.lName = lName;
this.uName = uName;
}
public String getuName() {
return uName;
}
public void setuName(String uName) {
this.uName = uName;
}
public String getfName() {
return fName;
}
public void setfName(String fName) {
this.fName = fName;
}
public String getlName() {
return lName;
}
public void setlName(String lName) {
this.lName = lName;
}
}
Also I call this using
public class TestClass {
public static void main(String[] args) {
Students students1 = new Students("xyz","abc","xyzAbc");
Students students2 = new Students("poi","son","poison");
Students students3 = new Students("yog","jos","yogjos");
Students students4 = new Students("xyz","abc","xyzAbc");
Students students5 = new Students("pon","son","xyzAbc");
Students students6 = new Students("yog","jos","someotherUName");
Students students7 = new Students("yog","jos","someotherUName2");
List studentList1 = new ArrayList();
List studentList2 = new ArrayList();
studentList1.add(students1);
studentList1.add(students2);
studentList1.add(students3);
studentList2.add(students4);
studentList2.add(students5);
studentList2.add(students6);
}
}
Now I want a filtered list which would contain only unique "uName" values. Thus I want the comparison between "uName" field of each list and remove common ones.
At the end I would want 2 filtered list for studentList1 and studentList2.
I read about the removeAll method, but it seems to work with List of Integer/String data and not with List of Objects (as in my case).
You can put your list to set and then (if you need) to take it back:
new ArrayList(new HashSet<String>(list)) creates list that contains only unique elements from source list.
1. If you want each list to have unique uName value, then you can use TreeSet from java.util.Collection along with Interface Comparator from java.util.Comparator.
2. If you want to merge both the list and have unique uName, then combine both the list and then use TreeSet and Comparator.
3. Comparator gives the flexibility to compare in more than one way...
You can still use removeAll if the Objects in the List implement equals() properly.
AbstractCollection, which is the base for most kind of List implementations (including ArrayList) uses contains() in its implementation of removeAll. ArrayList's implementation of contains relies on indexOf(), which lastly uses equals().
You could implement equals() in your Student class to specify that an Student is equal to another if and only their uName fields are equal.
Please note that equals has associated semantics (see its javadoc), and you should be careful when choosing how to implement it. Consider if two student instances really represent the same student when their uNames are equal. In my opinion, this sounds like a very specific requirement of how to sort these things out and should not impact the semantics of the class.
You'll be much better off with #AlexR or #KumarVivekMitra's approach.
Firstly, you should be typing your lists:
List<Students> studentList1 = new ArrayList<Students>();
Secondly, implement hashCode() and equals() on your Students class that both delegate to uName:
public boolean equals(Object o) {
return o instanceof Students && ((Students)o).uName.equals(uName);
}
public int hashCode() {
return uName.hashCode();
}
Now removeAll() will work just fine.
Another option is to use Set, which only allows unique values as determined by the equals() method. If you add the above methods to your class, you could just do this:
Set<Students> students = new HashSet<Students>();
then add what you like to it and there will only ever be unique uName students in it.
btw, you should name your class in the singular - ie Student not Students.
You could also try using the apache commons CollectionUtils.disjunction. (link: http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#disjunction(java.util.Collection, java.util.Collection))
Your class Student should override methods hashcode and equals, so youy object can be unique by name. Here you can read how to do that.
Also note that all objects which are maintained in some collection should have those methods overriden, so you always know how are they compared.
In next step you can use removeAll method or the solution with Set, it's up to you :-)
Set example
List<String> list = new ArrayList(new HashSet<String>(studentList)); // here is your unique list
Or you could use custom Comparator instead of overriding hashcode and equals but if you want your students always unique, you should override them
A full working solution could be as follows.
Class Student{
.....
......
.....
public boolean equals(Object o) {
return o instanceof Students && ((Students)o).uName.equals(uName);
}
public int hashCode() {
return uName.hashCode();
}
}
Set studentList1 = new hashSet();
Set studentList2 = new hashSet();
Put your elements in these Sets.
Also if you want unique non-matching elements in both Sets. then write as follows.
studentList1.removeAll(studentList2 );