I've a an interface and there are multiple implementations of the interface. There can be only one type of implementation for each interface type and I want to collect all the interface implementations per type, i.e.
Map<String, InterfaceExample>
public interface InterfaceExample {
String getType();
ClassA getClassA();
}
If I had to get in this form Map<String, List<InterfaceExample>> I would have done in the following way:
#Autowired
private List<InterfaceExample> interfaceExamples;
#Bean
public Map<String, List<IntefaceExample>> getExamples() {
return interfaceExamples.stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getType(), x))
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
}
Now that I've to ensure there is only one implementation per type I can do in following way:
#Bean
public Map<String, IntefaceExample> getExamples() {
Map<String, List<IntefaceExample>> examples = interfaceExamples.stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getType(), x))
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
Map<String, InterfaceExample> output = new HashMap<>();
examples.forEach((key, value) -> {
if(value.size() > 1) {
throw new RuntimeException("Wrongly configured!! ");
} else if(value.size() == 1) {
output.put(key, value.get(0));
}
});
return output;
}
Is there a different way to ensure that there is only implementation per type and create the bean in a "streamified way" without explicitly creating the output map?
After groupingby you can check if there are multiple beans of same type and collect them into List
List<InterfaceExample> res = interfaceExamples.stream().collect(Collectors.groupingBy(InterfaceExample::getType)).values()
.stream().map(value -> {
if (value.size() == 1) {
return value.get(0);
}
throw new RuntimeException("Wrongly configured!! ");
}).collect(Collectors.toList());
The best way is write a custom method that does the validation logic
public InterfaceExample conditionCheck(List<InterfaceExample> value) {
if (value.size() == 1) {
return value.get(0);
}
throw new RuntimeException("Wrongly configured!! ");
}
And then simply use stream
List<InterfaceExample> res = interfaceExamples.stream()
.collect(Collectors.groupingBy(InterfaceExample::getType))
.values()
.stream()
.map(this::conditionCheck)
.collect(Collectors.toList());
Related
I want to read mulitpart/formdata, one part is application/JSON. I can't get them to Map<String,String>, Is there any way to parse Part to String?
private Map<String, String> getFormData(String path, MultiValueMap<String, Part> partMultiValueMap) {
if (partMultiValueMap != null) {
Map<String, String> formData = new HashMap<>();
Map<String, Part> multiPartMap = partMultiValueMap.toSingleValueMap();
for (Map.Entry<String, Part> partEntry : multiPartMap.entrySet()) {
Part part = partEntry.getValue();
if (part instanceof FormFieldPart) {
formData.put(partEntry.getKey(), ((FormFieldPart) part).value());
} else {
String bodyString = bufferToStr(part.content());
formData.put(partEntry.getKey(), bodyString);
}
}
return formData;
}
return null;
}
extra Flux
private String bufferToStr(Flux<DataBuffer> content){
AtomicReference<String> res = new AtomicReference<>();
content.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
DataBufferUtils.release(buffer);
res.set(new String(bytes, StandardCharsets.UTF_8));
});
return res.get();
}
Subscribe is async; bufferToStr value may be null?
You could do it in non-blocking way with StringDecoder
Basically you could write your code to return Mono<Map<>>
Note: I'm using Pair class here to return key-value and later collect them to Map
Pair I'm using here is from package org.springframework.data.util.Pair
public Mono<Map<String, String>> getFormData(MultiValueMap<String, Part> partMultiValueMap) {
Map<String, Part> multiPartMap = partMultiValueMap.toSingleValueMap();
return Flux.fromIterable(multiPartMap.entrySet())
.flatMap(entry -> {
Part part = entry.getValue();
if (part instanceof FormFieldPart) {
return Mono.just(
Pair.of(entry.getKey(), ((FormFieldPart) part).value()) // return Pair
);
} else {
return decodePartToString(part.content()) // decoding DataBuffers to string
.flatMap(decodedString ->
Mono.just(Pair.of(entry.getKey(), decodedString))); // return Pair
}
})
.collectMap(Pair::getFirst, Pair::getSecond); // map and collect pairs to Map<>
}
private Mono<String> decodePartToString(Flux<DataBuffer> dataBufferFlux) {
StringDecoder stringDecoder = StringDecoder.textPlainOnly();
return stringDecoder.decodeToMono(dataBufferFlux,
ResolvableType.NONE,
MimeTypeUtils.TEXT_PLAIN,
Collections.emptyMap()
);
}
I want to return Mono of Boolean from the below method but getting compilation error with flatMap().
public Mono<Boolean> isMandatory(String country, String compliance,String referenceType) {
Flux<CustomReference> customRefFlux = getCustomsReferenceRule();
return Mono.just(customRefFlux.collectList().flatMap(customRefList -> {
customRefList.stream().anyMatch(customRef -> customRef.getType()
.equalsIgnoreCase(referenceType));
}));
}
It is internally calling:
public Flux<CustomReference> getCustomsReferenceRule() {
CustomReference c1 = new CustomReference();
c1.setCrKey("ECU");
c1.setType("MRN");
CustomReference c2 = new CustomReference();
c2.setCrKey("FUE");
c2.setType("FUE");
Flux<CustomReference> custRefFlux = Flux.just(c1, c2);
return custRefFlux;
}
The POJO class
#Data
public class CustomReference {
private String type;
private String crKey;
}
You likely don't want to mix streams and collections unless needed. If I'm following this should be a simple:
public Mono<Boolean> isMandatory(String country, String compliance,String referenceType) {
Flux<CustomReference> customRefFlux = getCustomsReferenceRule();
return customRefFlux.any(customRef -> customRef.getType()
.equalsIgnoreCase(referenceType));
}
Flux<CustomReference> customRefFlux = getCustomsReferenceRule();
return customRefFlux.collectList().flatMap(customRefList -> {
List<CustomReference> l = customRefList.stream()
.filter(customRef -> customRef.getType().equalsIgnoreCase(referenceType))
.collect(Collectors.toList());
if (!l.isEmpty())
return Mono.just(Boolean.TRUE);
else
return Mono.just(Boolean.FALSE);
});
Please help me with the Java 8 map - filter - sort - collect code.
Info.java
public class Info {
private final String name;
private final String desc;
private String version = null;
#Override
public boolean equals(Object that) {
if (that == null) {
return false;
}
if (that instanceof Info) {
Info other = (Info) that;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.desc, other.desc) &&
Objects.equals(this.version, other.version);
} else {
return false;
}
}
public boolean equalsWithoutVersion(Object that) {
if (that == null) {
return false;
}
if (that instanceof Info) {
Info other = (Info) that;
return Objects.equals(this.name, other.name) &&
Objects.equals(this.desc, other.desc);
} else {
return false;
}
}
#Override
public int hashCode() {
int hash = 13;
hash = (hash * 7) + name.hashCode();
hash = (hash * 7) + desc.hashCode();
if (version != null)
hash = (hash * 7) + version.hashCode();
return hash;
}
#Override
public String toString() {
String versionString = version == null ? "latest" : version;
return String.format("Name: %s Desc: %s Key Type: %s Version: %s", this.name, this.desc, this.keyType.name(), versionString);
}
}
Value.java
public class Value implements Comparable<Value> {
private String data;
private String version;
public Value(String version, String data) {
this.version = version;
this.data = data;
}
#Override
public int compareTo(Value o) {
return (Integer.parseInt(this.version) > Integer.parseInt(o.version)) ? -1
: (Integer.parseInt(this.version) < Integer.parseInt(o.version)) ? 1
: 0;
}
}
Cache.java
public class Cache {
private final Map<Info, Value> dataMap = new HashMap<>();
...
private Value getlatestVersionFromCache(Info info) {
List<Value> values = dataMap.entrySet().stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.sorted(Map.Entry.comparingByValue())
.map(x::getValue))
.collect(Collectors.toList());
return values.isEmpty() ? null : values.get(0);
}
}
The goal is to obtain the latest version of a record from the map. So first I am filtering entries from the map by comparing fields of Info without version. Then I am sorting the map by value based on version. Then I am collecting the values to a list. Then I can get the first element to get the latest version.
But I am getting the following compilation error on the filter statement:
Syntax error on token ")", ElidedSemicolonAndRightBrace expected
Well I doubt about your solution. I think you can do it in simple way. so first change version type to Integer in Value class(in compareTo() method you converted it to Integer). and also change method signature to Optional<Value> in getlatestVersionFromCache() method.
also I think you don't need to sort dataMap.
private Optional<Value> getlatestVersionFromCache(Info info) {
Map<Value,Integer> result = dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.collect(Collectors.toMap(Map.Entry::getValue, entry -> entry.getValue().getVersion(), Integer::min));
Optional<Value> latestValue = result.keySet()
.stream()
.min(Comparator.comparingInt(key -> key.getVersion()));
return latestValue;
}
Better solution is something like this:
dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.min(Comparator.comparingInt(entry -> entry.getValue().getVersion()))
.map(Map.Entry::getValue)
.orElse(null);
You have several omissions and errors in the code you posted, but the filter statement was actually fine.
The following passes compilation:
List<Value> values = dataMap.entrySet()
.stream()
.filter(p -> p.getKey().equalsWithoutVersion(info))
.sorted(Map.Entry.comparingByValue())
.map(Map.Entry::getValue) // was .map(x::getValue)) - x is not defined anywhere, so
// I assumed you meant Map.Entry::getValue
.collect(Collectors.toList());
class Info {
public static final BiPredicate<Info, Info> IS_EQUAL_IGNORE_VERSION =
(one, two) -> one.getName().equals(two.getName()) && one.getDesc().equals(two.getDesc());
private final String name;
private final String desc;
private String version;
}
private final Map<Info, Value> dataMap = new HashMap<>();
public Value getlatestVersionFromCache(Info info) {
Value value = null;
for (Map.Entry<Info, Value> entry : dataMap.entrySet())
if (Info.IS_EQUAL_IGNORE_VERSION.test(entry.getKey(), info))
if (value == null || Integer.parseInt(entry.getValue().getVersion()) > Integer.parseInt(value.getVersion()))
value = entry.getValue();
return value;
}
Notes:
I think that use a comparator to check that two objects are queal is not correct. You can use e.g. Predicate. And define it as a static method in the target class.
Pay attention on version. It seems that it should be an integer instead of String.
In your approach, compare two string is not correct, because it is used instead of equals method.
I think that using Java8 streams only because this is a stream is alow not correct.
I want to serialize an Object to Map by Moshi.Here is my codes by Gson
public static Map<String, String> toMap(Object obj, Gson gson) {
if (gson == null) {
gson = new Gson();
}
String json = gson.toJson(obj);
Map<String, String> map = gson.fromJson(json, new TypeToken<Map<String, String>>() {
}.getType());
return map;
}
And how to write by Moshi ?
Here's one way. Check out the toJsonValue doc here.
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
Object jsonStructure = adapter.toJsonValue(obj);
Map<String, Object> jsonObject = (Map<String, Object>) jsonStructure;
If you know the type of obj, it'd be better to look up the adapter of that type, rather than of Object. (The Object JsonAdadpter has to look up the runtime type on every toJson call.
#NanoJava8 solution crashes but can be made to work with a minor change using Map instead of HashMap
Type type = Types.newParameterizedType(Map.class, String.class, String.class);
JsonAdapter<Map<String,String>> adapter = moshi.adapter(type);
Map<String,String> map = adapter.fromJson(json);
As stated by Jesse in the answer Moshi support fields as Map but not HashMap.
In Kotlin:
val type = Types.newParameterizedType(
MutableMap::class.java,
String::class.java,
String::class.java
)
val adapter: JsonAdapter<Map<String, String>> = moshi.adapter(type)
val map: Map<String, String> = adapter.fromJson(responseJson)
Type type = Types.newParameterizedType(HashMap.class, String.class, String.class);
JsonAdapter<Map<String,String>> adapter = moshi.adapter(type);
Map<String,String> map = adapter.fromJson(json);
class HashMapJsonAdapter<K, V>(
private val keyAdapter: JsonAdapter<K>,
private val valueAdapter: JsonAdapter<V>
) : JsonAdapter<HashMap<K, V>>() {
#Throws(IOException::class)
override fun toJson(writer: JsonWriter, map: HashMap<K, V>?) {
writer.beginObject()
for ((key, value) in map ?: emptyMap<K, V>()) {
if (key == null) {
throw JsonDataException("Map key is null at ${writer.path}")
}
keyAdapter.toJson(writer, key)
valueAdapter.toJson(writer, value)
}
writer.endObject()
}
#Throws(IOException::class)
override fun fromJson(reader: JsonReader): HashMap<K, V>? {
val result = linkedMapOf<K, V>()
reader.beginObject()
while (reader.hasNext()) {
val name = keyAdapter.fromJson(reader)
val value = valueAdapter.fromJson(reader)
val replaced = result.put(name!!, value!!)
if (replaced != null) {
throw JsonDataException("Map key '$name' has multiple values at path ${reader.path} : $replaced and value")
}
}
reader.endObject()
return result
}
override fun toString(): String = "JsonAdapter($keyAdapter=$valueAdapter)"
companion object
}
Found difficulty to change this code according to java8 lambda expression.
The code removes student object which have same name and add that object into a single entry.
public List<Student> removeDuplicates(List<Student> studentList)
{
List<Student> newStudentList=new ArrayList<Student>();
List<String> studentNames= new ArrayList<String>();
studentList.stream().forEach(e->studentNames.add(e.getName()));
List<String> studentNameList= new ArrayList<String>();
for (Student student: studentList) {
int count=0;
for (String sname: studentNames)
{
if(student.getName().equals(sname)){
count++;
}
else{
count=0;
}
if(count==1)
{ if(!studentNameList.contains(student.getName())){
studentNameList.add(student.getName());
newStudentList.add(student);
}
}
}
}
}
Try this
public Stream<Student> removeDuplicates(List<Student> studentList) {
return studentList
.stream()
.collect(
Collectors.toMap(Student::getName, s -> s,
(fst, snd) -> fst)).values().stream();
}
Here you are using java-8 streams.
public List<Student> removeDuplicates(List<Student> studentList) {
return studentList.stream()
.map(Student::getName)
.distinct()
.map(x -> studentList.stream()
.filter(y -> y.getName().equals(x))
.findFirst()
.get())
.collect(Collectors.toList());
}
I assume you must end up with a List, otherwise you would have chosen to use a Set which by definition is a unique collection. A Set could still be part of the solution, however, doing something like
Set<String> tempUniqueValues = new HashSet<>();
List<String> listWithoutDupes = myList.stream()
.filter(tempUniqueValues::add) // filters out non-unique values
.collect(Collectors.toList());