I have a method looks like this.
public static <E extends Enum<E> & FieldEnum<E, V>, V>
void fieldValues(class<E> enumType,
Collection<? super V> fieldValues) {
for ( enumConstant : enumType.getEnumConstants()) {
fieldValues.add(enumConstant.fieldValue());
}
}
It might work.
List<Some> list = new ArrayList<>();
fieldValues(Some.class, list);
Now I want to change the method to return the given collection parameter(fieldValues). I did this.
public static <E extends Enum<E> & FieldEnum<E, V>, V, T super V>
Collection<T> fieldValues(Class<E> enumType,
Collection<T> fieldValues) {
for (E enumConstant : enumType.getEnumConstants()) {
fieldValues.add(enumConstant.fieldValue());
}
return fieldValues;
}
And the compiler complains.
com/github/.../lang/FieldEnums.java:[78,61] > expected
com/github/.../lang/FieldEnums.java:[78,62] illegal start of type
com/github/.../lang/FieldEnums.java:[78,69] '(' expected
How can I solve this? What is a proper way to return given collection so that I can do
List<Some> = fieldValues(Some.class, new ArrayList<Some>());
?
I found I just can
public static <E extends Enum<E> & FieldEnum<E, V>, V>
Collection<? super V> fieldValues(
Class<E> enumType, Collection<? super V> fieldValues) {
for (E enumConstant : enumType.getEnumConstants()) {
fieldValues.add(enumConstant.fieldValue());
}
return fieldValues;
}
Is there any other way better than this?
I'm answering for my own question so that anyone who has a similar problem can help themselves.
public static <E extends Enum<E> & FieldEnum<E, V>, V,
T extends Collection<? super V>>
T fieldValues(Class<E> enumType, T fieldValues) {
for (E enumConstant : enumType.getEnumConstants()) {
fieldValues.add(enumConstant.fieldValue());
}
return fieldValues;
}
Now I can do this.
List<Some> list = fieldValues(Some.class, new ArrayList<>());
Related
I have a little piece of code where i created my own java.util.stream realization.
I need to parametrize it using PECS rule. But either I didn't understand PECS rule well or my class designed bad - I don't know how to correctly implement it.
When I'm trying to implement it (? extends T) in filter() method realization, for example - I can't use foreach cycle.
Maybe you have some ideas? Thanks in advance.
public class Streams<T> {
private final List<T> list;
private List<T> resultList = new ArrayList<>();
private Streams(List<T> list) {
this.list = list;
}
public static <E> Streams<E> of(List<E> list) {
return new Streams<>(list);
}
public Streams<T> filter(Predicate<T> predicate) {
for (T elem : list) {
if (predicate.test(elem)) {
resultList.add(elem);
}
}
return this;
}
public Streams<T> transform(Function<? super T, ? extends T> function) {
for (T elem : resultList) {
resultList.set(resultList.indexOf(elem), function.apply(elem));
}
return this;
}
public <E, I> Map<E, I> toMap(Function<T, E> function1, Function<T, I> function2) {
HashMap<E, I> map = new HashMap<>();
for (T elem : resultList) {
map.put(function1.apply(elem), function2.apply(elem));
}
return map;
}
}
Although this Piece of code is working, it still grinds my gears:
public static <K, V> Map<K, V> entityListToIdMap(List<? extends BaseEntity<K>> list, Class<K> keyClass) {
Map<K, V> map = new TreeMap<K, V>();
if(list != null){
for (BaseEntity<K> item : list) {
map.put(item.getId(), (V) item);
}
}
return map;
}
What i'm missing is, to tell the method signature, that V extends BaseEntity<K>. This may also lead to the unchecked warning which makes it necessary to cast the item in the value to V.
How can I tell V that it must extend BaseEntity<K>?
based on accepted answer from #Ori Lentz, the complete solution:
public static <K, V extends BaseEntity<K>> Map<K, V> entityListToIdMap(List<V> list, Class<K> keyClass) {
Map<K, V> map = new TreeMap<K, V>();
if (list != null) {
for (V item : list) {
map.put(item.getId(), item);
}
}
return map;
}
Simply define it when you define the generic type:
public static <K, V extends BaseEntity<K>> Map<K, V> entityListToIdMap(..) {
I have a List<Foo> and want a Multimap<String, Foo> where we've grouped the Foo's by their getId() function.
I am using Java 8 and its almost awesome in that you can do:
List<Foo> foos = ...
Map<String, List<Foo>> foosById = foos.stream().collect(groupingBy(Foo::getId));
However, I have a good amount of code that wants a MultiMap<String, Foo> so this doesnt save me anything and I'm back to using a for-loop to create my Multimap. Is there a nice "functional" way that I am missing?
You can just use the Guava Multimaps factory:
ImmutableMultimap<String, Foo> foosById = Multimaps.index(foos, Foo::getId);
or wrap a call to Multimaps.index with a Collector<T, A, R> interface (shown below, in an unoptimized naive implementation).
Multimap<String, Foo> collect = foos.stream()
.collect(MultimapCollector.toMultimap(Foo::getId));
and the Collector:
public class MultimapCollector<T, K, V> implements Collector<T, Multimap<K, V>, Multimap<K, V>> {
private final Function<T, K> keyGetter;
private final Function<T, V> valueGetter;
public MultimapCollector(Function<T, K> keyGetter, Function<T, V> valueGetter) {
this.keyGetter = keyGetter;
this.valueGetter = valueGetter;
}
public static <T, K, V> MultimapCollector<T, K, V> toMultimap(Function<T, K> keyGetter, Function<T, V> valueGetter) {
return new MultimapCollector<>(keyGetter, valueGetter);
}
public static <T, K, V> MultimapCollector<T, K, T> toMultimap(Function<T, K> keyGetter) {
return new MultimapCollector<>(keyGetter, v -> v);
}
#Override
public Supplier<Multimap<K, V>> supplier() {
return ArrayListMultimap::create;
}
#Override
public BiConsumer<Multimap<K, V>, T> accumulator() {
return (map, element) -> map.put(keyGetter.apply(element), valueGetter.apply(element));
}
#Override
public BinaryOperator<Multimap<K, V>> combiner() {
return (map1, map2) -> {
map1.putAll(map2);
return map1;
};
}
#Override
public Function<Multimap<K, V>, Multimap<K, V>> finisher() {
return map -> map;
}
#Override
public Set<Characteristics> characteristics() {
return ImmutableSet.of(Characteristics.IDENTITY_FINISH);
}
}
Guava 21.0 introduced several methods that return Collector instances which will convert a Stream into a Multimap grouped by the result of applying a function to its elements. These methods are:
ImmutableListMultimap.toImmutableListMultimap — creates an ImmutableListMultimap
ImmutableSetMultimap.toImmutableSetMultimap — creates an ImmutableSetMultimap
Multimaps.toMultimap — Creates a Multimap using the given Supplier
ImmutableListMultimap<String, Foo> foosById = foos.stream().collect(
ImmutableListMultimap.toImmutableListMultimap(
Foo::getId, Function.identity()));
ImmutableSetMultimap<String, Foo> foosById = foos.stream().collect(
ImmutableSetMultimap.toImmutableSetMultimap(
Foo::getId, Function.identity()));
HashMultimap<String, Foo> foosById = foos.stream().collect(
Multimaps.toMultimap(
Foo::getId, Function.identity(), HashMultimap::create)
);
Example
private static final Comparator<A> PRODUCT_CODE_COMPARATOR = new Comparator<A>()
{
#Override
public int compare(final A o1, final A o2)
{
return o1.getCode().compareTo(o2.getCode());
}
};
public static <T extends A> List<T> sortProductsByCode(final Collection<T> productModels)
{
return sortProducts(productModels, PRODUCT_CODE_COMPARATOR);
}
private static <T> List<T> sortProducts(final Collection<T> t, final Comparator<T> comparator)
{
final List<T> variants = new ArrayList<T>(t);
Collections.sort(variants, comparator);
return variants;
}
Getting an error at return sortProducts(productModels, PRODUCT_CODE_COMPARATOR);
Can anyone help?
You need your declaration of sortProducts to be:
private static <T> List<T> sortProducts(final Collection<T> t,
final Comparator<? super T> comparator)
This allows the Comparator to compare T's or any super class of T. Or, in other words, the method will accept Comparator<T> or Comparator<A>.
One of my favourites from apache commons-collections was the LazyMap which would use a Transformer to instantiate values on the fly when doing map.get(newKey); // Will not return null!.
Why doesn't google collections have the same?
Hey look! It does!
It's called new MapMaker().makeComputingMap(Function<? super K, ? extends V> computer)
Awesome.
Note that map maker is a factory - you can make one, set all the object reference types, expansion properties (and even object expiration time!), and then go about creating lots of computing maps (or other types) with one line calls.
e.g. like pretty much everything else about the google-collections library, it's really good - once you've figured out where 'it' is
since 10.0, guava have a new class CacheBuilder instead, and it's gwt-compatible.
These are the differences.
I suggest to write your own
public class LazyMap<K, V> extends ForwardingMap<K, V> {
final Function<? super K, ? extends V> factory;
final Map<K, V> delegate;
public static <K, V> LazyMap<K, V> lazyMap(final Map<K, V> map, final Supplier<? extends V> supplier) {
return new LazyMap<>(map, supplier);
}
public static <K, V> LazyMap<K, V> lazyMap(final Map<K, V> map, final Function<? super K, ? extends V> factory) {
return new LazyMap<>(map, factory);
}
private LazyMap(final Map<K, V> map, final Function<? super K, ? extends V> factory) {
this.factory = factory;
this.delegate = map;
}
private LazyMap(final Map<K, V> map, final Supplier<? extends V> supplier) {
this.factory = Functions.forSupplier(supplier);
this.delegate = map;
}
#Override
protected Map<K, V> delegate() {
return delegate;
}
#Override
public V get(final Object key) {
if (delegate().containsKey(key) == false) {
#SuppressWarnings("unchecked")
final K castKey = (K) key;
final V value = factory.apply(castKey);
delegate().put(castKey, value);
return value;
}
return delegate().get(key);
}
}