Conflating Java streams - java

I have very big Stream of versioned documents ordered by document id and version.
E.g. Av1, Av2, Bv1, Cv1, Cv2
I have to convert this into another Stream whose records are aggregated by document id.
A[v1, v2], B[v1], C[v1, V2]
Can this be done without using Collectors.groupBy()? I don't want to use groupBy() because it will load all items in the stream into memory before grouping them. In theory, one need not load the whole stream in memory because it is ordered.

Here's a solution I came up with:
Stream<Document> stream = Stream.of(
new Document("A", "v1"),
new Document("A", "v2"),
new Document("B", "v1"),
new Document("C", "v1"),
new Document("C", "v2")
);
Iterator<Document> iterator = stream.iterator();
Stream<GroupedDocument> result = Stream.generate(new Supplier<GroupedDocument>() {
Document lastDoc = null;
#Override
public GroupedDocument get() {
try {
Document doc = Optional.ofNullable(lastDoc).orElseGet(iterator::next);
String id = doc.getId();
GroupedDocument gd = new GroupedDocument(doc.getId());
gd.getVersions().add(doc.getVersion());
if (!iterator.hasNext()) {
return null;
}
while (iterator.hasNext() && (doc = iterator.next()).getId().equals(id)) {
gd.getVersions().add(doc.getVersion());
}
lastDoc = doc;
return gd;
} catch (NoSuchElementException ex) {
return null;
}
}
});
Here are the Document and GroupedDocument classes:
class Document {
private String id;
private String version;
public Document(String id, String version) {
this.id = id;
this.version = version;
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
}
class GroupedDocument {
private String id;
private List<String> versions;
public GroupedDocument(String id) {
this.id = id;
versions = new ArrayList<>();
}
public String getId() {
return id;
}
public List<String> getVersions() {
return versions;
}
#Override
public String toString() {
return "GroupedDocument{" +
"id='" + id + '\'' +
", versions=" + versions +
'}';
}
}
Note that the resulting stream is an infinite stream. After all the groups there will be an infinite number of nulls. You can take all the elements that are not null by using takeWhile in Java 9, or see this post.

You can use groupRuns in the StreamEx library for this:
class Document {
public String id;
public int version;
public Document(String id, int version) {
this.id = id;
this.version = version;
}
public String toString() {
return "Document{"+id+version+ "}";
}
}
public class MyClass {
private static List<Document> docs = asList(
new Document("A", 1),
new Document("A", 2),
new Document("B", 1),
new Document("C", 1),
new Document("C", 2)
);
public static void main(String args[]) {
StreamEx<List<Document>> groups = StreamEx.of(docs).groupRuns((l, r) -> l.id.equals(r.id));
for (List<Document> grp: groups.collect(toList())) {
out.println(grp);
}
}
}
which outputs:
[Document{A1}, Document{A2}]
[Document{B1}]
[Document{C1}, Document{C2}]
I can't verify this doesn't consume the entire stream, but I cannot imagine why it would need to given what groupRuns is meant to do.

Would a Map<String, Stream<String>> help you with what you need ?
A - v1, v2
B - v1
C - v1, v2
String[] docs = { "Av1", "Av2", "Bv1", "Cv1", "Cv2"};
Map<String, Stream<String>> map = Stream.<String>of(docs).
map(s ->s.substring(0, 1)).distinct(). //leave only A B C
collect(Collectors.toMap( s1 -> s1, //A B C as keys
s1 ->Stream.<String>of(docs). //value is filtered stream of docs
filter(s2 -> s1.substring(0, 1).
equals(s2.substring(0, 1)) ).
map(s3 -> s3.substring(1, s3.length())) //trim A B C
));

Related

What is the best way to get the result through Java8 function?

I need to filter elements and then sort based on certain column. Post that I would need to find the unique entries based on combination of columns. Since it is file processing, pipe(|) is used as delimiter to denote the column value.
String s1= "12|Thor|Asgaurd|1000000|Avenger|Active"
String s2= "234|Iron man|New York|9999999|Avenger|Active"
String s3= "420|Loki|Asgaurd|||Inactive"
String s4= "12|Thor|Asgaurd Bank|1000000|Avenger HQ|Active"
Data first needs to be filtered based on the Active/Inactive status. Then it needs to be sorted based on 4th column. Lastly, the uniqueness needs to be maintained by combining column 1,2,3.
Expected Output =
"234|Iron man|New York|9999999|Avenger|Active"
"12|Thor|Asgaurd|1000000|Avenger|Active"
Creating a model class and parsing the string is the way to go, but if for some reaseon you don't want to do that you can do it this way:
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
List<String> result = Stream.of(s1, s2, s3, s4)
.filter(s -> s.split("\\|")[5].equals("Active"))
.sorted(Comparator.comparing(e -> e.split("\\|")[4]))
.collect(Collectors.toList());
First of all you should create an Object which represents your String data. Something like this:
public class MyObject {
private int id;
private String name;
private String location;
private Integer value;
private String category;
private String state;
public MyObject(String entry) {
String[] parts = entry.split("\\|");
if (parts.length != 6) {
throw new IllegalArgumentException("entry has not 6 parts");
}
id = Integer.parseInt(parts[0]);
name = parts[1];
location = parts[2];
try {
value = Integer.parseInt(parts[3]);
} catch (NumberFormatException ignored) {
}
category = parts[4];
state = parts[5];
}
// getters
#Override
public String toString() {
return String.join("|", String.valueOf(id), name, location, String.valueOf(value), category, state);
}
}
With this you can create a Stream of objects from your Strings and to the filter, sort and distinct operations afterwards:
Collection<MyObject> result = Stream.of(s1, s2, s3, s4)
.map(MyObject::new)
.filter(o -> "Active".equals(o.getState()))
.sorted(Comparator.comparing(MyObject::getValue).reversed())
.collect(Collectors.toMap(o -> Arrays.asList(o.getId(), o.getName()),
Function.identity(), (o1, o2) -> o1, LinkedHashMap::new))
.values();
result.forEach(System.out::println);
After the map operation you filter the values by state and sort them by column 4 (value in my case). At the end you collect all the values in a map for the distinct operation. Add all values you need distinction for to the Arrays.asList(). As values the map takes all the original values (Function.identity()). For duplicates we keep the first value ((o1, o2) -> o1) and we are using a LinkedHashMap to keep the order of the items. At the end wee use only the values of the map.
If you need a List instead of a Collection use new ArrayList(result).
The result will be this:
234|Iron man|New York|9999999|Avenger|Active
12|Thor|Asgaurd|1000000|Avenger|Active
It seems like you're unable to filter while everything is string only.
Try this,
create a new model class which can hold your columns.
Ex:
class MyData{
private String name;
private String city;
private String distance;
private String organization;
private String status;
//And create Getter Setter method for all above fields.
}
Now came to your main class where you can play with your code stuff.
Map<MyData> map = new HashMap<MyData>();
MyData myData = new MyData();
myData.setName("Thor");
myData.setCity("Asgaurd");
myData.setDistance("1000000");
myData.setOrganization("Avenger");
myData.setStatus("Active");
map.put(12, myData);
//Same thing for all other data (note: use the loop for data insertion in map)
Map<String, MyData> sorted = map.entrySet().stream().sorted(comparingByValue()).collect(toMap(e -> e.getKey(), e -> e.getValue().getName(), (e1, e2) -> e2,LinkedHashMap::new));
System.out.println("map after sorting by values: " + sorted);
You can solve your task this way:
Firstly, just create POJO(Plain Old Java Object) and override the toString() method.
class MarvelPerson {
private Integer id;
private String name;
private String origin;
private Integer point = null;
private String faction;
private String status;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public Integer getPoint() {
return point;
}
public void setPoint(Integer point) {
this.point = point;
}
public String getFaction() {
return faction;
}
public void setFaction(String faction) {
this.faction = faction;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(id);
builder.append("|");
builder.append(name);
builder.append("|");
builder.append(origin);
builder.append("|");
if(point != null) {
builder.append(point);
}
builder.append("|");
if(faction != null) {
builder.append(faction);
}
builder.append("|");
builder.append(status);
return builder.toString();
}
}
Then, you should write the parser from string to MarvelPerson. Side note: Carefully, my implementation is pretty basic, and I suppose it should be modified because I may not have foreseen some corner cases.
class PersonParser {
static MarvelPerson parse(String data) {
MarvelPerson person = new MarvelPerson();
String[] array = data.split("\\|", -1);
person.setId(Integer.parseInt(array[0]));
person.setName(array[1]);
person.setOrigin(array[2]);
if(!array[3].isEmpty()) {
person.setPoint(Integer.parseInt(array[3]));
}
if(!array[4].isEmpty()) {
person.setFaction(array[4]);
}
person.setStatus(array[5]);
return person;
}
}
And then your solution:
public class Test {
public static void main(String[] args) {
List<MarvelPerson> list = new ArrayList<>();
list.add(PersonParser.parse("12|Thor|Asgaurd|1000000|Avenger|Active"));
list.add(PersonParser.parse("234|Iron man|New York|9999999|Avenger|Active"));
list.add(PersonParser.parse("420|Loki|Asgaurd|||Inactive"));
list.add(PersonParser.parse("12|Thor|Asgaurd Bank|1000000|Avenger HQ|Actie"));
list.stream()
.filter(marvelPerson -> marvelPerson.getStatus().equals("Active"))
.sorted((o1, o2) -> o1.getPoint() <= o2.getPoint() ? 1 : -1)
.forEach(marvelPerson -> {
System.out.println(marvelPerson.toString());
});
}
}
The output to be printed:
234|Iron man|New York|9999999|Avenger|Active
12|Thor|Asgaurd|1000000|Avenger|Active

How to collect properties of List<Map> by unique property using MultiMap?

I have List of stories. Using unique property(id) I want to collect keyword and targeting as list of values. Can I do this with MultiMap? Or is there other library for this?
[{
id = 1,
title = Onboarding,
keyword = new joinee,
targeting = finance
}, {
id = 1,
title = Onboarding,
keyword = training,
targeting = HR
}]
The Desired output must like this :
{
id = 1,
title = Onboarding,
keyword = [new joinee,training], //may be keywords - plural
targeting = [HR,finance]
}
Sample my tried Code as follows:
package prac;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaPrac {
public static void main(String[] args) {
Multimap<Integer, Map> multiMap = ArrayListMultimap.create();
List<Map> stories=new ArrayList();
Map story1=new HashMap();
story1.put("id", 1);
story1.put("title", "Onboarding");
story1.put("keyword","new joinee");
story1.put("targeting","finance");
Map story2=new HashMap();
story2.put("id", 1);
story2.put("title", "Onboarding");
story2.put("keyword","training");
story2.put("targeting","HR");
stories.add(story1);
stories.add(story2);
System.out.println(stories);
stories.forEach((story) -> {
multiMap.put((Integer) story.get("id"), story);
});
}
}
A multimap can only store multiple values per key but what you want is to combine those multiple values so that you get one element that has the same id and title as well as a collection of keywords and targeting information. Thus it would probably be best to either have something like MultiStory or already have Story contain those collections.
I'd suggest using proper objects instead of just maps but with maps and Java 8 lambdas you could use compute() etc. to build maps that contain collections and combine maps that don't.
Here's an example of how you'd do it with maps. Note that this is very bad style and an example using proper pojos will follow:
Disclaimer: example based on the OP's code, not recommended (read text above)
//Problem 1: we don't know the type of the values, i.e. we could put anything for "id" etc.
Map<String, Object> story1=new HashMap<>();
story1.put("id", 1);
story1.put("title", "Onboarding");
story1.put("keyword","new joinee");
story1.put("targeting","finance");
Map<String, Object> story2=new HashMap<>();
story2.put("id", 1);
story2.put("title", "Onboarding");
story2.put("keyword","training");
story2.put("targeting","HR");
List<Map<String, Object>> stories=new ArrayList<>();
stories.add(story1);
stories.add(story2);
Map<Integer, Map<String, Object>> combined = new HashMap<>();
stories.forEach((story) -> {
//Problem 2: because we don't know the type of the values we need a lot of nasty casts
Map<String, Object> combinedStory = combined.computeIfAbsent( (Integer)story.get( "id" ), k -> new HashMap<String, Object>() );
combinedStory.put("id", story.get( "id" ) );
combinedStory.put("title", story.get( "title" ) );
//Problem 3: the combined map would look a lot like your "story" maps but would contain different types
((List<String>)combinedStory.computeIfAbsent( "keyword", v -> new List<String>() )).add( (String)story.get("keyword") );
((List<String>)combinedStory.computeIfAbsent( "targeting", v -> new List<String>() )).add( (String)story.get("targeting") );
});
Using POJOs
Here's a greatly simplified example of how you'd do it with proper Java objects (POJOs). Note that those are meant to resemble your code as much as possible and there are a lot of other issues but addressing those would be way too much here and better designed code would be a lot larger and probably harder to understand - after all it's just meant to show you a difference.
First let's define our classes (for simplicity I made the fields public, you'd normally not do that):
class Story {
public final int id;
public String title;
public String keyword;
public String targeting;
public Story(int storyId) {
id = storyId ;
}
}
class MultiStory {
public final int id;
public String title;
public Set<String> keywords = new HashSet<>();
public Set<String> targetingInfo = new HashSet<>();
public MultiStory( int storyId ) {
id = storyId ;
}
}
Then let's reiterate the code above:
Story story1=new Story( 1 );
story1.title = "Onboarding";
story1.keyword = "new joinee";
story1.targeting = "finance";
Story story2=new Story( 1 );
story2.title = "Onboarding";
story2.keyword = "training";
story2.targeting = "HR";
List<Story> stories=new ArrayList<>();
stories.add(story1);
stories.add(story2);
Map<Integer, MultiStory> combined = new HashMap<>();
stories.forEach((story) -> {
MultiStory multiStory = combined.computeIfAbsent( story.id, v -> new MultiStory( story.id ) );
multiStory.title = story.title;
multiStory.keywords.add( story.keyword );
multiStory.targetingInfo.add( story.targeting );
});
As you can see, there are no casts needed and it's clear what fields are available (though not necessarily filled) which makes it easier to reason about the code and spot errors (the compiler can help a lot here which it couldn't to in the example that uses maps).
Here is a solution using classes to represent the story and tags:
public static void main(String[] args) {
TagsCollector app = new TagsCollector();
app.go();
}
private void go() {
List<Story> stories = createStories();
System.out.println(stories);
Map<Long, Tags> tagsById = collectTags(stories);
tagsById.forEach((aLong, tags) -> System.out.println(tags));
}
private List<Story> createStories() {
return Arrays.asList(
new Story(1, "Onboarding", "new joinee", "finance"),
new Story(1, "Onboarding", "training", "HR")
);
}
private Map<Long, Tags> collectTags(List<Story> stories) {
Map<Long, Tags> tagsById = new HashMap<>();
stories.forEach(s -> {
Tags tags = tagsById.computeIfAbsent(s.id, v -> new Tags(s));
tags.getKeywords().add(s.getKeyword());
tags.getTargetings().add(s.getTargeting());
});
return tagsById;
}
Class used to represent the Story:
public class Story {
private final long id;
private final String title;
private final String keyword;
private final String targeting;
public Story(long id, String title, String keyword, String targeting) {
this.id = id;
this.title = title;
this.keyword = keyword;
this.targeting = targeting;
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getKeyword() {
return keyword;
}
public String getTargeting() {
return targeting;
}
#Override
public String toString() {
return String.format("Story %s, title=%s, keyword=%s, targeting=%s", id, title, keyword, targeting);
}
}
Class used to represent the Tags:
public class Tags {
private final long id;
private final String title;
private final List<String> keywords = new ArrayList<>();
private final List<String> targetings = new ArrayList<>();
Tags(Story story) {
this.id = story.id;
this.title = story.title;
}
public List<String> getKeywords() {
return keywords;
}
public List<String> getTargetings() {
return targetings;
}
#Override
public String toString() {
return String.format("Tags for id %s, title:%s: keywords=%s, targetings=%s", id, title, keywords, targetings);
}
}
Output
[Story 1, title=Onboarding, keyword=new joinee, targeting=finance, Story 1, title=Onboarding, keyword=training, targeting=HR]
Tags for id 1, title:Onboarding: keywords=[new joinee, training], targetings=[finance, HR]
Yes, you can do that with a Multimap. First I would define a pojo for Story in order to make things clearer:
public class Story {
private int id;
private String title;
private String keyword;
private String targeting;
//getters setters
}
Second you need to define a key with hashcode and equals.
public static class StoryKey {
private final int id;
private final String title;
public StoryKey(int id, String title) {
this.id = id;
this.title = title;
}
//getters
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StoryKey storyKey = (StoryKey) o;
if (id != storyKey.id) return false;
return title != null ? title.equals(storyKey.title) : storyKey.title == null;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (title != null ? title.hashCode() : 0);
return result;
}
The code will look like:
ArrayListMultimap<StoryKey, Story> multiMap = ArrayListMultimap.create();
List<Story> stories = new ArrayList();
Story story1 = new Story();
story1.setId(1);
story1.setTitle("Onboarding");
story1.setKeyword("training");
story1.setTargeting("HR");
Story story2 = new Story();
story2.setId(1);
story2.setTitle("Onboarding");
story2.setKeyword("new joinee,");
story2.setTargeting("finance");
stories.add(story1);
stories.add(story2);
System.out.println(stories);
stories.
forEach((story) -> {
multiMap.put(new StoryKey(story.getId(), story.getTitle()), story);
});
multiMap.keys().forEach(key ->
System.out.println(
"id =" + key.getId() +
" title =" + key.getTitle()+
"keyword =" + multiMap.get(key).stream().map(story->story.getKeyword()).collect(Collectors.toList()).toString()+
"targeting ="+ multiMap.get(key).stream().map(story->story.getTargeting()).collect(Collectors.toList()).toString())
);

how to get the required result, the result is array list?

I have an array of object, which is basically an Arraylist.
The input and output data look like this
data= [
{
id:1,
parent:0
},
{
id:2,
parent:0
},
{
id:3,
parent:1
},
{
id:4,
parent:1
},
{
id:5,
parent:3
},
{
id:6,
parent:3
}
]
if parent of any object is equal to id, then that will become the children the result should look like this.
[
{
id:1,
parent:0,
children:[
{
id:3,
parent:1,
children:[ {
id:5,
parent:3
},
{
id:6,
parent:3
}
]
},
{
id:4,
parent:1
}
]
},
{
id:2,
parent:0
}
]
Because i am not able to access the inner element of the array of object by using for loop, i am not able to put condition on it.
i try using something like this
for(Map <String,Object> individual_object: data) {
}
How to do this?
My complete code:
try {
taxonDao.openCurrentSession();
List<Object[]> taxonList = taxonDao.list(parent, classificationId, taxonIds, expand_taxon);
List res = new ArrayList();
Map<String, Object> m1 = new HashMap<String, Object>();
TaxonUI ui = new TaxonUI();
Map<Long, Map<String, Object>> m2 = new HashMap<Long, Map<String, Object>>();
for (Object[] t : taxonList) {
Map<String, Object> m = new HashMap<String, Object>();
ui.setId((Long) t[0]);
ui.setTaxonid((Long) t[0]);
ui.setClassification((Long) t[4]);
ui.setPath((String) t[3]);
ui.setText((String) t[1]);
ui.setRank((Integer) t[2]);
if(t[5]!=null){
ui.setParent((Long)t[5]);
}
m.put("id", ui.getId());
m.put("taxonid", ui.getId());
m.put("text", ui.getText());
m.put("rank", ui.getRank());
m.put("path", ui.getPath());
m.put("classification", ui.getClassification());
m.put("parent", ui.getParent());
res.add(m);
}
return res;
} catch (Exception e) {
throw e;
} finally {
taxonDao.closeCurrentSession();
}
First you need an Item class to store the id and parent id:
(The toString() is for debugging so you can print the input and output list to verify the result)
public class Item {
private final int parentId;
private final int id;
private final java.util.List<Item> children = new ArrayList<>();
public Item(int parentId, int id) {
this.parentId = parentId;
this.id = id;
}
public void addChild(Item child) {
children.add(child);
}
#Override
public String toString() {
String result = "id: " + id + ", parent: " + parentId;
if (children.isEmpty() == false) {
result += ", children: " + children;
}
return result;
}
public int getId() {
return id;
}
public int getParentId() {
return parentId;
}
}
Then as usual, a Main class and main method to prepare the input and process it:
public class Main {
public static void main(String[] args) {
java.util.List<Item> inputItems = createInputItems();
java.util.List<Item> oututItems = processItemsToParentChildren(inputItems);
System.out.println(oututItems);
}
The create input method is straight forward:
private static List<Item> createInputItems() {
java.util.List<Item> result = new ArrayList<>();
result.add(new Item(0, 1));
result.add(new Item(0, 2));
result.add(new Item(1, 3));
result.add(new Item(1, 4));
result.add(new Item(3, 5));
result.add(new Item(3, 6));
return result;
}
Then you need a method to map id to the corresponding item:
private static Map<Integer, Item> prepareIdItemMap(List<Item> items) {
HashMap<Integer, Item> result = new HashMap<>();
for (Item eachItem : items) {
result.put(Integer.valueOf(eachItem.getId()), eachItem);
}
return result;
}
And then the critical part, to add the correct child items to their parent, or to root of the list if parent id is 0:
private static List<Item> processItemsToParentChildren(List<Item> items) {
java.util.List<Item> result = new ArrayList<>();
Map<Integer, Item> idItemMap = prepareIdItemMap(items);
for (Item eachItem : items) {
int parentId = eachItem.getParentId();
if (parentId == 0) {
result.add(eachItem);
} else {
idItemMap.get(Integer.valueOf(parentId)).addChild(eachItem);
}
}
return result;
}
Why not define a Class with the following fields (Let's define this class name as ParentChildClass:
public class ParentChildClass{
int id;
ParentChildClass parent;
ArrayList<ParentChildClass> children;
ParentChildClass(ParentChildClass parent){
this.children = new ArrayList<ParentChildClass>();
if(parent != null){
this.parent = parent;
}
}
//getter and setters
//method to add a child
public void addChild(ParentChildClass child){
children.add(child);
}
}
Now wrap this inside a HashMap<Integer, ParentChildClass> to easily access all the elements by their ids. Iterate through the object you currently have and start adding these elements one by one in the HashMap by creating instances of ParentChildClass. If the parent is already not present in the HashMap, find the element with parent's id, include it in the HashMap by creating its new instance (Don't add the parent recursively now, as you'll anyway come later to this element while iterating, when you can add the parent for this object as well). After doing this, set this newly created parent element as the parent of the Child element, and proceed.
In the end, you'll be left with a map that has references to all the parent child objects which can be used to access and element in O(1), its parent and all its children (in the arraylist).
You can connect elements to their parents by first looping them through and collecting their IDs to a map. This will allow you to then find a parent for each element after you loop them through again to construct the hierarchy that you described. Note that you also need to collect to elements without parents (called roots below.
List<MyElement> list = asList(
new MyElement(1, 0),
new MyElement(2, 0),
new MyElement(3, 1),
new MyElement(4, 1),
new MyElement(5, 3),
new MyElement(6, 3));
Map<Integer, MyElement> elementsById = new HashMap<>();
for (MyElement element : list) {
elementsById.put(element.getId(), element);
}
List<MyElement> roots = new ArrayList<>();
for (MyElement element : elementsById.values()) {
if (elementsById.containsKey(element.getParent())) {
MyElement parent = elementsById.get(element.getParent());
parent.addChild(element);
} else {
roots.add(element);
}
}
System.out.println(roots);
MyElement class for reference
public class MyElement {
private int id;
private int parent;
private List<MyElement> children = new ArrayList<>();
public MyElement(int id, int parent) {
this.id = id;
this.parent = parent;
}
#Override
public String toString() {
return "{id=" + id + ", children=" + children + "}";
}
public void addChild(MyElement child) {
if (child.parent != id) {
throw new IllegalArgumentException("Expected parent id " + id + " got " + child.parent);
}
children.add(child);
}
public int getId() {
return id;
}
public int getParent() {
return parent;
}
public List<MyElement> getChildren() {
return Collections.unmodifiableList(children);
}
}
If the inner fields of your object is private, you cannot access them by name.
you cannot write object.field
You need to have accessors to get their value
your code will be object.getFiled()

Map with LIst using Collectors.toMap in Java 8

I like convert below code to java stream,
HashMap<String, List<Data>> heMap = new HashMap<String, List<Data>>();
for (Data heData : obj) {
String id = heData.getData().getId() + heData.getPlanData().getCode()
+ heData.getPlanData().getId();
if (!heMap.containsKey(id)) {
CitizenHElist = new ArrayList<Data>();
CitizenHElist.add(heData);
heMap.put(id, CitizenHElist);
} else {
heMap.get(id).add(heData);
}
}
I tried the below code using stream, but i am not succeed on this.
heMap=obj.stream().collect(Collectors.toMap(t->getKey(t), obj.stream().collect(Collectors.toList())));
private String getKey(Data heData){
String id = heData.getData().getId() + heData.getPlanData().getCode()
+ heData.getPlanData().getId();
return id;
}
This is the job for groupingBy collector:
import static java.util.stream.Collectors.groupingBy;
Map<String, List<Data>> heMap = obj.stream().collect(groupingBy(d -> getKey(d)));
Note that this will use some unspecified implementations of Map and List. Currently, it happens to be HashMap and ArrayList, but that might change in the future.
Grouping on the bases of a field -
import java.util.*;
import java.util.stream.*;
public class Main
{
public static void main (String[]args)
{
System.out.println ("Hello World");
List < Data > dataList = getDataList();
System.out.println (dataList);
Map < String, List < Data >> dataMap =
dataList.stream ().collect (Collectors.groupingBy (d->d.code));
System.out.println (dataMap);
}
static List < Data > getDataList(){
List < Data > dataList = new ArrayList <> ();
dataList.add (new Data (1, "Prince", "102"));
dataList.add (new Data (2, "Rahul", "102"));
dataList.add (new Data (3, "Sunny", "103"));
dataList.add (new Data (4, "Mitul", "104"));
dataList.add (new Data (5, "Amit", "105"));
dataList.add (new Data (6, "Ashish", "105"));
return dataList;
}
}
class Data
{
int id;
String name;
String code;
public Data (int id, String name, String code)
{
this.id = id;
this.name = name;
this.code = code;
}
public String toString ()
{
return String.format ("id:%s,name:%s,code:%s", id, name, code);
}
}
not sure your data structure but you want to do something like below, which is working.
import java.util.*;
import java.util.stream.Collectors;
class Data {
String stud_id;
String stud_name;
String stud_location;
public Data(String string, String string2, String string3) {
this.stud_id=string;
this.stud_location=string2;
this.stud_name=string3;
}
public Object getData() {
return this.stud_id;
}
}
class Temp3
{
public static void main(String args[])
{
Map<String, List<Data>> heMap=new HashMap<String, List<Data>>();
Data data1=new Data("1","11","111");
Data data2=new Data("2","22","222");
List<Data> obj=new ArrayList<Data>();
obj.add(data1);
obj.add(data2);
for (Data heData : obj)
{
String id = "2";
if (!heMap.containsKey(id))
{
ArrayList<Data> CitizenHElist = new ArrayList<Data>();
CitizenHElist.add(heData);
heMap.put(id, CitizenHElist);
}
else
{
heMap.get(id).add(heData);
}
}
heMap=obj.stream().collect(Collectors.groupingBy(w -> w.stud_location));
System.out.println(heMap);
}
}

Why am I able to pass Long to Map.get() method which is not keyed on Longs

I've run into some funky behavior with generics and I was wondering if someone could shed some light as to why this is happening. To start, I have a class Foo which has a field id. The hashCode method on Foo just returns the id. In another class I create a Map<Foo, Double> bar = new HashMap<Foo, Double().
Then, at a later part of the code the strangeness starts, and I am able to do the following (simplified here):
Long baz = new Long(1);
bar.get(baz);
So, my question is, Why doesn't the compiler and catch this and report it as an error?
EDIT: I made one mistake in my initial question in that get is the method that works, not put. I have posted the full code below.
Map<WebPage, Double> scoresForPhrase = new HashMap<WebPage, Double>();
// Now that we have a list of matching docs, we can calculate the
// Score of each word in the phrase for each document
for (String term: phrase.getWords()) {
TreeSet<Posting> wordPostings = wordMap.get(term);
for(Long doc: matchingDocs) {
if (docDenomScores.get(doc) == null) {
docDenomScores.put(doc, getDocTotal(doc));
}
// The set is of postings, which are compared by docId, so
// we need a temporary one to enable searching
Posting temp = new Posting(doc, new ArrayList<Integer>());
Posting wordPosting = wordPostings.ceiling(temp);
WebPage page = (WebPage) mWebpageDb
.getPageIdToWebPageTable().get(doc);
score = getTermScore(wordPosting, page,
wordPostings.size());
score = score * queryTermWeights.get(term);
Double curScore = scoresForPhrase.get(doc);
}
}
As for the Foo class, it is:
public class WebPage implements Serializable {
private static final long serialVersionUID = -4907557806357281837L;
private String mUrl;
private int mMaxTf;
private long mPageId;
private long mLastTimeUpdated;
private List<Long> mParentIds;
private long mContentLength;
private String mTitle;
private List<Long> mChildren;
private List<String> mAllUrls;
public WebPage(String url, long pageId, long lastTimeUpdated,
List<Long> parentIds, long contentLength, String title, List<Long> children,
List<String> allUrls) {
super();
this.mUrl = url;
this.mPageId = pageId;
this.mLastTimeUpdated = lastTimeUpdated;
this.mParentIds = parentIds;
this.mContentLength = contentLength;
this.mTitle = title;
this.mChildren = children;
this.mAllUrls = allUrls;
this.mMaxTf = 0;
}
public void setUrl(String mUrl) {
this.mUrl = mUrl;
}
public void setPageId(int mPageId) {
this.mPageId = mPageId;
}
public void setLastTimeUpdated(long mLastTimeUpdated) {
this.mLastTimeUpdated = mLastTimeUpdated;
}
public void setParentIds(List<Long> mParentId) {
this.mParentIds = mParentId;
}
public void setContentLength(long mContentLength) {
this.mContentLength = mContentLength;
}
public void setChildren(List<Long> mChildren) {
this.mChildren = mChildren;
}
public void setAllUrls(List<String> allUrls) {
this.mAllUrls = allUrls;
}
public void setMaxTf(int newTf) {
this.mMaxTf = newTf;
}
public String getUrl() {
return mUrl;
}
public long getPageId() {
return mPageId;
}
public long getLastTimeUpdated() {
return mLastTimeUpdated;
}
public List<Long> getParentIds() {
return mParentIds;
}
public long getContentLength() {
return mContentLength;
}
public List<Long> getChildren() {
return mChildren;
}
public String getTitle() {
return mTitle;
}
public List<String> getAllUrls() {
return mAllUrls;
}
public int getMaxTf() {
return mMaxTf;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof WebPage)) {
return false;
} else {
return ((WebPage)o).mPageId == mPageId;
}
}
#Override
public int hashCode() {
return (int)mPageId;
}
public String toString() {
return mUrl;
}
}
So two things. First, remember that due to type-erasure there is no runtime checking of generic types. The Map<Foo, Double> simply becomes Map<Object, Object>.
Second, with regards to a compiler warning or error, you should get a warning or error if bar is declared of type Map<Foo, Double>. But if it is declared as Map, no warning or error. My guess is that bar is defined as Map bar.
UPDATE
The reason there is no error on get is that by definition get takes an Object not the generic type. It is one of the odd things about the interface.
Map.get
Your Map<Foo, Double> might have been casted to Map:
Map<Foo, Double> barOriginal = new HashMap<Foo, Double();
// ...
Map bar = barOriginal;
// ...
Long baz = new Long(1);
bar.put(baz, new Double(1));

Categories