public static <E extends Enum<E>> List<E> enumHeadAsList(final Class<E> val, final E topValue)
{
// My code
}
I want to make a method that accept any enum i give and i set a default value of that enum to do some order check and then return all values as list. Basically i want set head of an enum and return values. So far i managed to do this but i don't know how to continue.
I want call the following method like:
enumHeadAsList(BuffType.class, BuffType.SELF);
This should do the trick:
public static <E extends Enum<E>> List<E> enumHeadAsList(final E topValue)
{
E[] values = topValue.getDeclaringClass().getEnumConstants();
if (topValue.ordinal() != 0) {
E oldTop = values[0];
values[0] = topValue;
values[topValue.ordinal()] = oldTop;
}
return List.of(values);
}
This code assumes that you don't care too much about the order of the remaining elements (as the top element is simply swapped with the previously first one).
If you want to keep the relative order of the other values intact, then you'll have to tweak replace the if above with this code block:
final int topValueIndex = topValue.ordinal();
if (topValueIndex != 0) {
System.arraycopy(values, 0, values, 1, topValueIndex);
values[0] = topValue;
}
Note that a dedicated Class<E> parameter is not needed if topValue is not allowed to be null, because we can get the enum class from topValue.
If using a Java version before Java 9, then you can replace List.of() with Arrays.asList() with the difference that the resulting List would be mutable.
EnumSet makes this easy:
public static <E extends Enum<E>> List<E> enumHeadAsList(final Class<E> val, final E topValue)
{
List<E> values = new ArrayList<>();
values.add(topValue);
values.addAll(EnumSet.complementOf(EnumSet.of(topValue)));
return values;
}
complementOf returns a collection of all enum values except those present in the argument. So, complementOf(EnumSet.of(topValue)) is all values except topValue.
Related
I have a for loop which applies a Function for each element of a list. The list could be of type String or Long. Depending on a condition, I need to either iterate the String list or the Long list. Is there a way where I can do it without using if - else?
public static <T, S> T weirdFunction(Function<S, T> myFunction, Boolean convertToLong){
List<String> stringList = fetchFromSomewhere();
if(convertToLong){
// fetch list of longs from elsewhere
}
for (<String or Long> id : List<String> or List<Long>) { // choose list type based on some method param
myFunction.apply(id); // myFunction is of type Function
}
}
Having a function that returns another function, to be applied "recursively"1, makes this tricky.
Here is how it can be done:
#FunctionalInterface
interface ChainFunction<T> extends Function<T, ChainFunction<T>> {
// Nothing to add
}
static <T> ChainFunction<T> applyFunctionChain(List<T> list, ChainFunction<T> myFunction) {
for (T id : list) {
myFunction = myFunction.apply(id);
}
return myFunction;
}
The method can be called with String and with Long, and any other type for that matter.
1) "Recursive" is not the right word, but I don't know what it's called.
This presents one approach to call a different function per type without using an if statement. It requires one to build a map of different classes the list contains, each with a particular function. The containing class that houses the methods is SpecialList There is a caveat in this. Since the return value is cast to its actual type it can't be assigned in the loop unless it is returned as an Object. If the return type is not needed, then the return type could be set to void and the FunctionalInterface in the map could be a Consumer
Map<Class<?>, Function<Object, Object>> map = new HashMap<>();
map.put(String.class, SpecialList::stringFnc);
map.put(Long.class, SpecialList::longFnc);
List<Object> list = List.of("String1", 123L, "String2", 1234L,
29292L, "String4");
for (Object ob : list) {
map.get(ob.getClass()).apply(ob);
}
public static String stringFnc(Object str) {
String v = (String)str;
// now do something with v.
System.out.println("String : " + v);
return v;
}
public static Long longFnc(Object lng) {
long v = (Long)lng;
// now do something with v
System.out.println("Long : " + v);
return v;
}
prints
String : String1
Long : 123
String : String2
Long : 1234
Long : 29292
String : String4
I have a map of class names to their enum class and I have method that parses a string like "SomeEnum.FIRST" into the actual object. But Enum.valueOf doesn't accept Class<? extends Enum<?>> while the map cannot store Class<T extends Enum<T>>.
For the code, the map looks something like this:
private static final HashMap<String, Class<? extends Enum<?>>> enumsMap;
static {
enumsMap = new HashMap<>();
// These are two DIFFERENT enum classes!
registerEnum(SomeEnum.class);
registerEnum(AnotherEnum.class);
}
private static void registerEnum(Class<? extends Enum<?>> enumClass) {
enumsMap.put(enumClass.getSimpleName(), enumClass);
}
And here is the parser (removed unnecessary code):
public <T extends Enum<T>> Object[] parse(List<String> strParameters) {
Object[] parameters = new Object[strParameters.size()];
for (int i = 0; i < parameters.length; i++) {
String strParameter = strParameters.get(i);
int delim = strParameter.lastIndexOf('.');
String className = strParameter.substring(0, delim - 1);
String enumName = strParameter.substring(delim + 1);
Class<T> enumClass = (Class<T>) enumsMap.get(className);
parameters[i] = Enum.valueOf(enumClass, enumName);
}
return parameters;
}
And now if I call this parse, my IDE (Android Studio) tells me, that "Unchecked method 'parse(List)' invocation", and afaik this is because of that generic type. If I remove it in parse, it wouldn't compile but the warning disappears. Is there a good way around it?
If you have enums like:
enum Foo {
A, B, C
}
enum Bar {
D, E, F
}
Then you can implement the kind of map you're talking about with the following code.
class MyEnums {
private final Map<String, Class<? extends Enum<?>>> map = new HashMap<>();
public void addEnum(Class<? extends Enum<?>> e) {
map.put(e.getSimpleName(), e);
}
private <T extends Enum<T>> T parseUnsafely(String name) {
final int split = name.lastIndexOf(".");
final String enumName = name.substring(0, split);
final String memberName = name.substring(split + 1);
#SuppressWarnings("unchecked")
Class<T> enumType = (Class<T>) map.get(enumName);
return Enum.valueOf(enumType, memberName);
}
public Object parse(String name) {
return parseUnsafely(name);
}
public Object[] parseAll(String... names) {
return Stream.of(names)
.map(this::parse)
.collect(toList())
.toArray();
}
}
This does not get around an unchecked cast, though; it only hides it from you temporarily. You can see where where SuppressWarnings is used to muffle the warning about enumType. It's generally good practice to apply the warning suppression in as limited a scope as possible. In this case, it's for that single assignment. While this could be a red flag in general, in the present case we know that the only values in the map are, in fact, enum classes, since they must have been added by addEnum.
Then, it can be used as:
MyEnums me = new MyEnums();
me.addEnum(Foo.class);
me.addEnum(Bar.class);
System.out.println(me.parse("Foo.A"));
System.out.println(me.parse("Bar.E"));
System.out.println(Arrays.toString(me.parseAll("Foo.B", "Bar.D", "Foo.C")));
which prints:
A
E
[B, D, C]
You'll notice that I broke parseUnsafely and parse into separate methods. The reason that we don't want to expose parseUnsafely directly is that it makes a guarantee by its return type that we cannot actually enforce. If it were exposed, then we could write code like
Bar bar = me.parseUnsafely("Foo.B");
which compiles, but fails at runtime with a cast class exception.
There is no safe way to have Map values whose generic type depends on the corresponding key.
You can, however, store the enum constants yourself:
private static final Map<String, Map<String, ?>> enumsMap;
static {
enumsMap = new HashMap<>();
// These are two DIFFERENT enum classes!
registerEnum(SomeEnum.class);
registerEnum(AnotherEnum.class);
}
private static <T extends Enum<T>> void registerEnum(Class<T> enumClass) {
Map<String, ?> valuesByName =
EnumSet.allOf(enumClass).stream().collect(
Collectors.toMap(Enum::name, Function.identity()));
enumsMap.put(enumClass.getSimpleName(), valuesByName);
}
public Object[] parse(List<String> strParameters) {
Object[] parameters = new Object[strParameters.size()];
for (int i = 0; i < parameters.length; i++) {
String strParameter = strParameters.get(i);
int delim = strParameter.lastIndexOf('.');
String className = strParameter.substring(0, delim);
String enumName = strParameter.substring(delim + 1);
Map<String, ?> enumValues = enumsMap.get(className);
parameters[i] = enumValues.get(enumName);
if (parameters[i] == null) {
throw new IllegalArgumentException("Class " + className
+ " does not contain constant " + enumName);
}
}
return parameters;
}
What I’ve changed:
enumsMap is now Map<String, Map<String, ?>>. Each value is a Map of enum constants keyed by constant name. ? is sufficient; there is no benefit to remembering that the constant values are enums, since parse returns Object[].
registerEnum has a generic type, to guarantee its argument is a valid enum type. Instead of storing the class argument, it stores that enum’s constants.
parse doesn’t need a generic type, since it returns Object[].
parse does not use any methods of Enum, so generic type safety is no longer a concern.
I fixed a bug: strParameter.substring(0, delim); instead of delim - 1. You want the entire substring up to but not including the period.
I have this assignment so I'm not looking for an answer but I just want to understand basically how can we compare two variables of the same generic type in Java using the equals method. I've searched stackoverflow for some answers but only could find using the compareTo method where your class extends Comparable I think.
package Maillon;
import java.util.ArrayList;
public class ListeAssociativeChainee<K, E> implements IListeAssociative <C, E> {
private Node<K> keys;
private Node<Node<E>> elements;
private int nbrKeys;
public ListeAssociativeChainee () {
keys = new Node<>(null);
elements = new Node<>(null);
nbrKeys = 0;
}
}
What it currently looks like:
What I am trying to do:
My problem hits when I try to delete an element by looking for it through the associative list with the key and element given in the parametres such as:
public boolean supprimer(K key, E element) {
/*****/
}
However, I also have another overloaded method that goes like this:
public boolean supprimer(K key, int index) {
/*****/
}
This one says that it deletes an element at the index specified using the key and an index point given in parametres.
Now say that I create an associative list as follow:
IListAssociative<String, Integer> testList = new ListAssociativeChainee();
where IListAssocivative is an interface implemented in ListAssociativeChainee.
now if my list contains values of type int as follow:
key1 -> [1, 2, 4, 5, 6]
key2 -> [5, 6, 8]
key3 -> [0, 7, 23]
and I want to use the delete method
testList.supprimer(K key, E element);
and I say:
testList.supprimer("key3", 23);
instead it's going to call
testList.supprimer(K key, int index);
and that's because I'm using an int and not an Integer from what I understand.
now say that I do this:
testList.supprimer("key3", new Integer(23));
it will call
testList.supprimer(K key, E element);
but will look for values of type Integer that contains 23.
however, my method elementExists() will always return false in that case.
Any tips on how I could do it?
EDIT:
here's my elementExists()
private boolean elementExiste(Node<Node<E>> listeElements, E element)
{
Node<Node<E>> m = listeElements;
Node<E> tmp = m.info();
boolean existe = false;
while(tmp != null && tmp.info() != element) {
tmp = tmp.next();
}
if(tmp != null && tmp.info() == element) {
existe = true;
}
return existe;
}
I know that my problem here is that I'm using == to compare and int and an Integer and this is wrong but I'm not sure how to implement the equals() method to compare
When your code provides ambiguous methods like your two delete, the best solution is always to rename the method themselves. Instead of supprimer(Key, int), rename it to supprimerParIdx(Key, int).
You could also check the instance of your Object to make sure it's not an Integer :
if (element instanceof Integer) {
throw new RuntimeException("Integers are forbidden by my law!");
}
or instead of an Exception, unbox and call the other method
this(key, element.intValue());
This is, of course, dirty in everyway.
On a side note :
new Integer(23) : Never do this, use Integer.valueOf(23) instead, same goes for most Boxed primitives.
In Java, I have several SortedSet instances. I would like to iterate over the elements from all these sets. One simple option is to create a new SortedSet, such as TreeSet x, deep-copy the contents of all the individual sets y_1, ..., y_n into it using x.addAll(y_i), and then iterate over x.
But is there a way to avoid deep copy? Couldn't I just create a view of type SortedSet which would somehow encapsulate the iterators of all the inner sets, but behave as a single set?
I'd prefer an existing, tested solution, rather than writing my own.
I'm not aware of any existing solution to accomplish this task, so I took the time to write one for you. I'm sure there's room for improvement on it, so take it as a guideline and nothing else.
As Sandor points out in his answer, there are some limitations that must be imposed or assumed. One such limitation is that every SortedSet must be sorted relative to the same order, otherwise there's no point in comparing their elements without creating a new set (representing the union of every individual set).
Here follows my code example which, as you'll notice, is relatively more complex than just creating a new set and adding all elements to it.
import java.util.*;
final class MultiSortedSetView<E> implements Iterable<E> {
private final List<SortedSet<E>> sets = new ArrayList<>();
private final Comparator<? super E> comparator;
MultiSortedSetView() {
comparator = null;
}
MultiSortedSetView(final Comparator<? super E> comp) {
comparator = comp;
}
#Override
public Iterator<E> iterator() {
return new MultiSortedSetIterator<E>(sets, comparator);
}
MultiSortedSetView<E> add(final SortedSet<E> set) {
// You may remove this `if` if you already know
// every set uses the same comparator.
if (comparator != set.comparator()) {
throw new IllegalArgumentException("Different Comparator!");
}
sets.add(set);
return this;
}
#Override
public boolean equals(final Object o) {
if (this == o) { return true; }
if (!(o instanceof MultiSortedSetView)) { return false; }
final MultiSortedSetView<?> n = (MultiSortedSetView<?>) o;
return sets.equals(n.sets) &&
(comparator == n.comparator ||
(comparator != null ? comparator.equals(n.comparator) :
n.comparator.equals(comparator)));
}
#Override
public int hashCode() {
int hash = comparator == null ? 0 : comparator.hashCode();
return 37 * hash + sets.hashCode();
}
#Override
public String toString() {
return sets.toString();
}
private final static class MultiSortedSetIterator<E>
implements Iterator<E> {
private final List<Iterator<E>> iterators;
private final PriorityQueue<Element<E>> queue;
private MultiSortedSetIterator(final List<SortedSet<E>> sets,
final Comparator<? super E> comparator) {
final int n = sets.size();
queue = new PriorityQueue<Element<E>>(n,
new ElementComparator<E>(comparator));
iterators = new ArrayList<Iterator<E>>(n);
for (final SortedSet<E> s: sets) {
iterators.add(s.iterator());
}
prepareQueue();
}
#Override
public E next() {
final Element<E> e = queue.poll();
if (e == null) {
throw new NoSuchElementException();
}
if (!insertFromIterator(e.iterator)) {
iterators.remove(e.iterator);
}
return e.element;
}
#Override
public boolean hasNext() {
return !queue.isEmpty();
}
private void prepareQueue() {
final Iterator<Iterator<E>> iterator = iterators.iterator();
while (iterator.hasNext()) {
if (!insertFromIterator(iterator.next())) {
iterator.remove();
}
}
}
private boolean insertFromIterator(final Iterator<E> i) {
while (i.hasNext()) {
final Element<E> e = new Element<>(i.next(), i);
if (!queue.contains(e)) {
queue.add(e);
return true;
}
}
return false;
}
private static final class Element<E> {
final E element;
final Iterator<E> iterator;
Element(final E e, final Iterator<E> i) {
element = e;
iterator = i;
}
#Override
public boolean equals(final Object o) {
if (o == this) { return true; }
if (!(o instanceof Element)) { return false; }
final Element<?> e = (Element<?>) o;
return element.equals(e.element);
}
}
private static final class ElementComparator<E>
implements Comparator<Element<E>> {
final Comparator<? super E> comparator;
ElementComparator(final Comparator<? super E> comp) {
comparator = comp;
}
#Override
#SuppressWarnings("unchecked")
public int compare(final Element<E> e1, final Element<E> e2) {
if (comparator != null) {
return comparator.compare(e1.element, e2.element);
}
return ((Comparable<? super E>) e1.element)
.compareTo(e2.element);
}
}
}
}
The inner workings of this class are simple to grasp. The view keeps a list of sorted sets, the ones you want to iterate over. It also needs the comparator that will be used to compare elements (null to use their natural ordering). You can only add (distinct) sets to the view.
The rest of the magic happens in the Iterator of this view. This iterator keeps a PriorityQueue of the elements that will be returned from next() and a list of iterators from the individual sets.
This queue will have, at all times, at most one element per set, and it discards repeating elements. The iterator also discards empty and used up iterators. In short, it guarantees that you will traverse every element exactly once (as in a set).
Here's an example on how to use this class.
SortedSet<Integer> s1 = new TreeSet<>();
SortedSet<Integer> s2 = new TreeSet<>();
SortedSet<Integer> s3 = new TreeSet<>();
SortedSet<Integer> s4 = new TreeSet<>();
// ...
MultiSortedSetView<Integer> v =
new MultiSortedSetView<Integer>()
.add(s1)
.add(s2)
.add(s3)
.add(s4);
for (final Integer i: v) {
System.out.println(i);
}
I do not think that is possible unless it is some special case, which would require custom implementation.
For example take the following two comparators:
public class Comparator1 implements Comparator<Long> {
#Override
public int compare(Long o1, Long o2) {
return o1.compareTo(o2);
}
}
public class Comparator2 implements Comparator<Long> {
#Override
public int compare(Long o1, Long o2) {
return -o1.compareTo(o2);
}
}
and the following code:
TreeSet<Long> set1 = new TreeSet<Long>(new Comparator1());
TreeSet<Long> set2 = new TreeSet<Long>(new Comparator2());
set1.addAll(Arrays.asList(new Long[] {1L, 3L, 5L}));
set2.addAll(Arrays.asList(new Long[] {2L, 4L, 6L}));
System.out.println(Joiner.on(",").join(set1.descendingIterator()));
System.out.println(Joiner.on(",").join(set2.descendingIterator()));
This will result in:
5,3,1
2,4,6
and is useless for any Comparator operating on the head element of the given Iterators.
This makes it impossible to create such a general solution. It is only possible if all sets are sorted using the same Comparator, however that cannot be guaranteed and ensured by any implementation which accept SortedSet objects, given multiple SortedSet instances (e.g. anything that would accept SortedSet<Long> instances, would accept both TreeSet objects).
A little bit more formal approach:
Given y_1,..,y_n are all sorted sets, if:
the intersect of these sets are an empty set
and there is an ordering of the sets where for every y_i, y_(i+1) set it is true that y_i[x] <= y_(i+1)[1] where x is the last element of the y_i sorted set, and <= means a comparative function
then the sets y_1,..,y_n can be read after each other as a SortedSet.
Now if any of the following conditions are not met:
if the first condition is not met, then the definition of a Set is not fulfilled, so it can not be a Set until a deep copy merge is completed and the duplicated elements are removed (See Set javadoc, first paragraph:
sets contain no pair of elements e1 and e2 such that e1.equals(e2)
the second condition can only be ensured using exactly the same comparator <= function
The first condition is the more important, because being a SortedSet implies being a Set, and if the definition of being a Set cannot be fulfilled, then the stronger conditions of a SortedSet definitely cannot be fulfilled.
There is a possibility that an implementation can exists which mimics the working of a SortedSet, but it will definitely not be a SortedSet.
com.google.common.collect.Sets#union from Guava will do the trick. It returns an unmodifiable view of the union of two sets. You may iterate over it. Returned set will not be sorted. You may then create new sorted set from returned set (new TreeSet() or com.google.common.collect.ImmutableSortedSet. I see no API to create view of given set as sorted set.
If your concern is a deep-copy on the objects passed to the TreeSet#addAll method, you shouldn't be. The javadoc does not indicate it's a deep-copy (and it certainly would say so if it was)...and the OpenJDK implementation doesn't show this either. No copies - simply additional references to the existing object.
Since the deep-copy isn't an issue, I think worrying about this, unless you've identified this as a specific performance problem, falls into the premature optimization category.
i'm trying to make a generic function in Java to find out the maximum similarity between an ArrayList and a list from an ArrayList of ArrayLists.
public static int maxSimilarity(ArrayList<?> g,
ArrayList<ArrayList<?>> groups){
int maxSim = 0;
for(ArrayList<?> g2:groups){
int sim = similarity(g, (ArrayList<?>) g2);
if(sim > maxSim)
maxSim = sim;
}
return maxSim;
}
However, when i try to call it in my main function, it show an incompatible error
ArrayList<ArrayList<Points>> cannot be converted to ArrayList<ArrayList<?>>
I don't understand, i tought all objects can be represented by the ? sign. Also, it works in my similarity function, between two ArrayLists:
public static int similarity(ArrayList<?> g1, ArrayList<?> g2){
int total = 0;
for(Object o1:g1){
for(Object o2:g2){
if(o1.equals(o2))
total++;
}
}
return total;
}
Instead of a wildcard, declare a generic value:
public <T> static int maxSimilarity(List<T> g, List<? extends List<T>> gs);
Change your method signature to:
public static int maxSimilarity(ArrayList<?> g, ArrayList<? extends ArrayList<?>> groups)
And in general prefer using interface types, instead of actual implementations (more flexible, less code):
public static int maxSimilarity(List<?> g, List<? extends List<?>> groups)
[edit] Based on the suggestion with the type variables, to make this super-generic, it should be:
public static <T> int maxSimilarity(List<? extends T> g, List<? extends List<? extends T>> groups)
Notice that ? extends T. This way, you can use e.g.
List<List<StringBuilder>> groups = // ...
List<String> g = // ...
maxSimilarity(g, groups);
(StringBuilder and String are a CharSequence, so they can be compared).
If you want to compare lists of similar objects, you should introduce a method type parameter
public static <T> int maxSimilarity(List<T> g, List<List<T>> groups) {
because it's nearly useless comparing completely different objects.
Try declaring your method:
public static <T>int maxSimilarity(ArrayList<T> g, ArrayList<ArrayList<T>> groups)
Hope it helps.