J'ai un blocage mental en essayant de créer une structure de données qui suit le modèle:
Map est un bloc de construction principal et T est soit Map soit en tant qu'opérateur de terminal < code> Liste Java , cette idée vient de langages fonctionnels comme F # ou Haskell .
J'ai recherché SO mais jusqu'à présent je n'ai rien trouvé qui corresponde à mon idée en Java .
4 Réponses :
Oui: vous pouvez faire quelque chose comme ceci:
public abstract class T {
...
}
public class NonTerminal extends T {
private Map<String,T> map = new HashMap<>();
...
}
public class Terminal extends T {
private List<String> list;
---
}
J'irais avec une interface plutôt qu'une classe abstraite. Il n'est pas nécessaire de limiter l'héritage pour cela.
J'ajouterais un constructeur privé à la classe abstraite et placerais les sous-classes en tant que classes imbriquées statiques pour le faire ressembler à un type de données algébrique.
Recréer des éléments de programmation fonctionnelle en Java n'est pas vraiment une bonne idée (du moins pas en Java 8, je ne connais pas Java 11).
Vous pouvez faire quelque chose comme ceci:
class EitherMapOrList {
private Map<String, EitherMapOrList> map;
private List<String> list;
public EitherMapOrList(Map<String, EitherMapOrList> map) {
this.map = map;
}
public EitherMapOrList(List<String> list) {
this.list = list;
}
// you can remove the optionals here and use null directly.
public Optional<Map<String, EitherMapOrList>> getMap() {
return Optional.ofNullable(map);
}
public Optional<List<String>> getList() {
return Optional.ofNullable(list);
}
}
Et puis créez une Map .
Mais j'imagine que ce serait pénible d'utiliser cette chose en Java.
Si vous voulez traduire haskell
class Map<T> {
String key;
T value;
Map<T> left;
Map<T> right;
}
en java, vous pouvez utiliser:
data Map a = Branch { key :: String, value :: a, left :: Map a, right :: Map a} | MapNul
vous n'avez pas besoin de MapNul en java car vous pouvez utiliser null à la place.
Vous pouvez utiliser une seule Map où la valeur peut être une interface de marqueur avec deux implémentations
public List<String> getValues(String key) {
KeyOrValue keyOrValue;
List<String> values = null;
do {
keyOrValue = map.get(key);
if(keyOrValue instanceof Key) {
// is a key, so iterate further
key = ((Key) keyOrValue).key;
} else if(keyOrValue instanceof Value) {
// is a value, so get the values out and set the key to null to break the loop
values = ((Value) keyOrValue).values;
key = null;
}
} while(key != null);
// return the values, may be null due to nothing being found
return values;
}
Vous pouvez alors simplement créer un méthode de recherche qui s'appelle de manière récursive puis renvoie la valeur une fois qu'elle a atteint la fin:
private final Map<String, KeyOrValue> map = ...
public List<String> getValues(String key) {
KeyOrValue keyOrValue = map.get(key);
if(keyOrValue instanceof Key) {
// is a key, so use recursion to get the value
key = ((Key) keyOrValue).key;
return getValues(key);
} else if(keyOrValue instanceof Value) {
// is a value, so just return the value it holds
return ((Value) keyOrValue).values;
} else {
// no mapping was found for "key"
return null;
}
}
Vous pouvez également faire de même sans récursivité:
interface KeyOrValue {}
class Key implements KeyOrValue {
private String key;
}
class Value implements KeyOrValue {
private List<String> values;
}
L'interface du marqueur n'est cependant pas vraiment nécessaire, vous pouvez obtenir le même résultat si vous utilisez simplement Map où la valeur peut être un String ou une List puis les vérifications instanceof devraient également être adaptées, mais j'aime l'approche avec un interface plus