items is a List<List<String>>. When I add reversed, I see a compile error Cannot resolve method 'get' in 'Object'. I get this on x.get(0). Why do I get this and how can I get the reverse order? I would just like to sort the list based on the item name.
List<List<String>> items = new ArrayList<>();
List<String> item2 = new ArrayList<>();
item2.add("item2");
item2.add("3");
item2.add("4");
List<String> item1 = new ArrayList<>();
item1.add("item1");
item1.add("10");
item1.add("15");
List<String> item3 = new ArrayList<>();
item3.add("item3");
item3.add("17");
item3.add("8");
items.add(item1);
items.add(item2);
items.add(item3);
items.stream().sorted(Comparator.comparing(x -> x.get(0)).reversed()).collect(Collectors.toList())
Java's type inference fails here. When running Comparator.comparing(x -> x.get(0)).reversed(), it is not able to infer the types of the Comparator from the return type because of the .reversed(). Since they cannot be inferred, Java just uses Object.
In order to fix that, you need to somehow specify the type that is compared.
This can be done explicitly with Comparator.<List<String>, String>> comparing(x->x.get(0)).reversed(). Here, List<String>, String> are type arguments for the Comparator.comparing call. This means they specify the values of the generic types at compile-time so they don't have to be inferred. The first type parameter (List<String>) is the type to compare and second parameter (String) is the Comparable that is extracted from the type to compare (the result of List#get).
Alternatively, you could also specify it in the lambda expression's parameters and let Java infer it from that: Comparator.comparing((List<String> x) -> x.get(0)).reversed(). Since Java knows that x is of the type List<String>, it is able to infer the type of the lambda (Function<List<String>,String>) and the Comparator.comparing method.
When Sequenced Collections are added to Java, you could also use use method references. If you use Comparator.comparing(List::getFirst).reversed(), Java knows that the type to compare is a List and is able to use the get method. Note that List::getFirst is not yet part of the JDK as of the time of writing this.
As #Slaw mentioned in the comments, you could also use an entirely different approach by getting rid of the .reversed and including it in the Comparator.comparing like this: Comparator.comparing(x -> x.get(0), Comparator.reverseOrder()). The second parameter is another Comparator that is used for comparing the values extracted from the lambda x -> x.get(0).
List<List<String>> sorted_list = items.stream()
.sorted(Comparator.comparing(x -> x.get(0), Comparator.reverseOrder()))
.collect(Collectors.toList());
Related
Everywhere I look, I see the typing of Collections done like this:
Set<String> set = new HashSet<String>();
However, I define my Collections like this
Set<String> set = new HashSet();
and I still get the type checking (and my way is just cleaner to look at).
Maybe this has something to do when creating generic collections? But, let's say I just want nothing more than a HashSet of Strings, then is not
Set<String> set = new HashSet();
enough?
You are using a generic class when creating a new collection.
The generic class must get the generic type it encapsulates, otherwise it is considered a Raw Type.
The proper declaration of the collection value, should therefore be:
Set<String> mySet = new HashSet<>();
Your JVM will be able to infer the generic type being used in your HashSet thanks to the declaration on Set<String>
Most IDEs (Eclipse and ItelliJ, for example) will have their linters configured to provide a warning when using a Raw Type class. This warning can be suppressed, but that is considered a bad practice.
References:
Generic Types
Handling Raw Types and Type Safety
What is a raw type and why shouldn't we use it
Bonus:
What is "Linting"?
In this particular use case, where you are passing no arguments to the constructor, it makes no difference.
However, if you were using a version of the constructor that took a parameter whose type depends on the type variable, then if you use the raw type constructor, the compiler will not check the type properly.
This incorrect code compiles due to the raw type, and will cause heap pollution:
List<Integer> foo = Arrays.asList(1, 2, 3);
Set<String> set = new HashSet(foo);
Whereas using the proper type argument appropriately prevents it from compiling:
List<Integer> foo = Arrays.asList(1, 2, 3);
Set<String> set = new HashSet<String>(foo);
By the way, you can use the diamond operator in Java 7+ if you don't want to be so verbose:
Set<String> set = new HashSet<>();
Let's say I have an interface called Importable which has multiple implementations and one of them is ImportableImpl.
I have a list of a class Staging which has a generic type: Importable.
Via java 8 stream, I would like to transform the list:
List<Staging<Importable>> list1
to a list:
List<Staging<ImportableImpl>> list2
I am already sure that the list list1 is a list of Staging<ImportableImpl>, so no ClassCastException can occurs.
I don't know if java authorizes this kind of transformation with generics type.
I wanted to try something like this:
list1.stream()
.map(Staging::getImportable) // retrieve generic type
.map(ImportableImpl.class::cast)
.collect(Collectors.toList())
But with this I will get a List<ImportableImpl> and not a List<Staging<ImportableImpl>>.
I really don't realise what the purpose of your attempts, but you are not allowed to turn a List<Staging<Importable>> to a List<Staging<ImportableImpl>> regardless of whatever is in the first list. These types are inconvertible.
The only reasonable way to do that (with a warning, though) is:
#SuppressWarnings("unchecked") // if you are sure that all elements are ImportableImpl
list.stream().map(i -> (Staging<ImportableImpl>) i).collect(Collectors.toList());
where the list has a type of List<Staging<? extends Importable>>, not a List<Staging<Importable>>.
Of course, it can be written with a method reference:
list.stream().map(Staging.class::<ImportableImpl>cast).collect(Collectors.toList());
It looks to me that you've got to create a new staging object...
This answer assumes you can create a Staging object by using new Staging<>(thing). Is this what you need?
list1.stream()
.map(s -> new Staging<ImportableImpl>(s.getImportable()))
.collect(Collectors.toList())
If I got this right than you basically just want to express that your list1 actually already is a List of type List<Staging<ImportableImpl>>. Thus throwing away the abstraction provided by your interface Importable.
As at runtime none of the Generic types exist you should be able to just cast List<Staging<ImportableImpl>> list2 = (List) list1. This is an unchecked cast (usung the raw form as an intermediate) and will remove some of the safety the type system provides. It might break if someone added an object of type Staging<AnotherImportableImpl> to list1.
Using fancy constructs like streams does not change the situation. Basically you just want to tell the compiler that you're sure you know something it cannot check.
I had a HashMap called map which stored Characters as the key and Integers as the value which I then stored into an ArrayList called entries using the following code:
Set<Entry<Character, Integer>> s = map.entrySet();
ArrayList<Entry<Character, Integer>> entries = new ArrayList<>(s);
Now I am trying to sort these entries based on the Integer value, not the key. I tried to use a lambda expression to implement the Comparator interface, but it is not working. This is my code:
Collections.sort(sortedEntries, (sortedEntries.get(0), sortedEntries.get(1)) -> {
sortedEntries.get(0).getValue().compareTo(sortedEntries.get(1).getValue())
});
These are the errors I get:
Multiple markers at this line
Syntax error, insert ")" to complete Expression
The method sort(List, Comparator) in the type Collections is not applicable for the arguments (ArrayList>, Map.Entry, Map.Entry)
Syntax error on token "->", ; expected
You could sort the list by the values this way:
list.sort(Comparator.comparing(Entry::getValue));
This is strictly as per your code pls see
Collections.sort(sortedEntries, (Entry<Character,Integer> o1, Entry<Character,Integer> o2)-> {return o1.getValue().compareTo(o2.getValue());});
There is nothing special about the map, the list, or the type you're trying to sort. You sort such a list the same way you sort any List whose elements are of a type that doesn't implement Comparable - by using a Comparator, like you're attempting to do. So you're on the right track.
Perhaps if you aren't comfortable with lambda expressions, try implementing the Comparator interface instead. The type system will force you to "do it right", rather than relying on you to get it right so it can infer the types for you.
(I'm using the entry's key here, but you can just as well use the value)
public class EntryKeyComparator implements Comparator<Entry<Character,Integer>> {
#Override
public int compare(Entry<Character,Integer> a, Entry<Character,Integer> b) {
return a.getKey().compareTo(b.getKey());
}
}
Now, if you really want to use lambda expressions, you need only look at the compare method in this implementation. The lambda expression must take two parameters, a and b, and it must return an int. Thus, you'd get something like:
Collections.sort(list, (a, b) -> a.getKey().compareTo(b.getKey()));
But since you're already in the Java 8 world, you might as well use the static comparing() method on the Comparator class. It takes as argument a Function that extracts the key you want to use to compare the elements by. In my examples, I've compared the entries by their keys via the getKey(). We can reference this method with the :: operator, so you end up with an expression like this (using the new sort() method on the List interface):
list.sort(Comparator.comparing(Entry::getKey));
You can sort the entities using Java Stream API like this:
Set<Entry<Character, Integer>> s = map.entrySet();
List<Entry<Character, Integer>> sortedEntries = s.stream()
.sorted((a, b)-> Integer.compare(a.getValue(), b.getValue()))
.collect(Collectors.toList());
I have a list of ScheduleContainer objects and in the stream each element should be casted to type ScheduleIntervalContainer. Is there a way of doing this?
final List<ScheduleContainer> scheduleIntervalContainersReducedOfSameTimes
final List<List<ScheduleContainer>> scheduleIntervalContainerOfCurrentDay = new ArrayList<>(
scheduleIntervalContainersReducedOfSameTimes.stream()
.sorted(Comparator.comparing(ScheduleIntervalContainer::getStartDate).reversed())
.filter(s -> s.getStartDate().withTimeAtStartOfDay().isEqual(today.withTimeAtStartOfDay())).collect(Collectors
.groupingBy(ScheduleIntervalContainer::getStartDate, LinkedHashMap::new, Collectors.<ScheduleContainer> toList()))
.values());
It's possible, but you should first consider if you need casting at all or just the function should operate on subclass type from the very beginning.
Downcasting requires special care and you should first check if given object can be cast down by:
object instanceof ScheduleIntervalContainer
Then you can cast it nicely by:
Use functional method to cast like ScheduleIntervalContainer.class::cast
So, the whole flow should look like:
collection.stream()
.filter(ScheduleIntervalContainer.class::isInstance)
.map(ScheduleIntervalContainer.class::cast)
// other operations
Do you mean you want to cast each element?
scheduleIntervalContainersReducedOfSameTimes.stream()
.map(sic -> (ScheduleIntervalContainer) sic)
// now I have a Stream<ScheduleIntervalContainer>
Or you could use a method reference if you feel it is clearer
.map(ScheduleIntervalContainer.class::cast)
On a performance note; the first example is a non-capturing lambda so it doesn't create any garbage, but the second example is a capturing lambda so could create an object each time it is classed.
What is the difference between List and List<?>? I know I can't add any element to the List<?>. I have a code:
List<String> myList = new ArrayList<String>();
processList(myList);
processListGeneric(myList);
public static void processList(List myList) {
Iterator it = myList.iterator();
while(it.hasNext())
System.out.println(it.next());
}
public static void processListGeneric(List<?> myList) {
Iterator<?> it = myList.iterator();
while(it.hasNext())
System.out.println(it.next());
}
The name of the two methods cannot be the same, because it causes compile time error. So is there any difference in these two approaches?
Both do the same, but in second case compiler is informed that you really want a list with no type bounds and raises no warnings. If you are working with Java 5 or later you are encouraged to use second approach.
The difference is that you can't add anything to a List<?>, since it's a List of an unknown type.
For example, you are prevented from doing this:
List<Integer> listOfInt = new ArrayList<Integer>();
List<?> list = listOfInt;
list.add("hello?"); // Compile-time error
You can add anything you want to the base type List since the type of the list items is not checked.
List<?> (pronounced "collection of unknown")is a collection whose element type matches anything. It's called a wildcard type for obvious reasons.
Refer to the following code
List<String> myList = new ArrayList<String>();
myList.add("John");
String name = myList.get(0);
System.out.println(name); //print John
List<?> myListNew = myList;
myListNew.add("Sam");//Compile time error
String nameNew = myListNew.get(0);//Compile time error
Object newName = myListNew.get(0);
System.out.println(newName);//Prints John
Since we don't know what the element type of myListNew stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.
On the other hand, given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.
List is raw type and List< ?> is wildcard type. Take look http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
Both of them behave same. Using parametrized notation you are just avoiding any warnings in Java5 and above. You cannot have both the syntax in same java file because due to type erasure compiled unit has two methods with exactly same signature in same class file and thus violating language rules.
Following is what you would be getting from compiler:
Method processList(List) has the same erasure processList(List) as another method in type ...
Just writing List without a type parameter is deprecated, but otherwise the same as writing List<Object>. So the question becomes "What's the difference between List<Object> and List<?> and which one should I use?".
As you already know, you can't add (anything other than null) to a List<?>, so if you need to add to a list, you should use a List<Object> (or a more specific type where applicable, of course). On the other hand, a method that takes a List<Object>, only accepts List<Object>s and not any lists that contain a subclass of Object. That is, it would not accept a List<String>. If the method takes a List<?> however, it accepts any kind of list. So if you don't need to add to the list, you should use List<?> as it is more general.
The second method uses generics (introduced in Java 5).
One important distinction is the <?> represents a single type, not any object like this:
List<? extends Object> myList
So you could say that using the first method (without the wildcard syntax) is more flexible, as you'd be able to add any object to your List. Although, you'll get a (compiler) warning that you declaration should be parameterized.
Using the <?> unbounded wildcard syntax will avoid the warning, but you're telling the compiler it could be a list of any type, instead of actually using generics to enforce type safety. You are strongly encouraged to use generics to assist you in making your application type safe.
If, for example, you know that the list should only ever contain String objects, declare it thus:
List<String> myList
Then you'll avoid unnecessary casting of use of the instanceof operator, etc.
Here's a brief tutorial on generics in Java, for your information:
http://javarevisited.blogspot.co.uk/2011/09/generics-java-example-tutorial.html