如何使用MultiMap通过唯一属性收集List <map>的属性?

时间:2018-11-21 09:19:21

标签: java list

我有故事列表。我想使用唯一的property(id)来收集关键字和定位作为值列表。我可以使用MultiMap吗?还是为此提供其他图书馆?

[{
    id = 1,
    title = Onboarding,
    keyword = new joinee,
    targeting = finance
}, {
    id = 1,
    title = Onboarding,
    keyword = training,
    targeting = HR
}]

所需的输出必须像这样:

{
    id = 1,
    title = Onboarding,
    keyword = [new joinee,training], //may be keywords - plural
    targeting = [HR,finance]
} 

按如下所示对我尝试过的代码进行采样:

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);
        });
    }
}

3 个答案:

答案 0 :(得分:4)

一个多图每个键只能存储多个值,但是您想要的是将这些多个值组合在一起,以便获得一个具有相同ID和标题以及一组关键字和定位信息的元素。因此,最好使用类似MultiStory的东西或已经包含这些集合的Story

我建议您使用适当的对象而不是仅使用地图,而对于地图和Java 8 lambda,您可以使用compute()等来构建包含集合的地图,并合并不包含地图的地图。

这是您如何使用地图的示例。请注意,这是非常糟糕的样式,下面将使用适当的pojos进行示例:

免责声明:基于OP代码的示例,不建议使用(请参见上面的文字)

//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") );
});

使用POJO

这是如何使用适当的Java对象(PO​​JO)进行简化的示例。请注意,这些代码旨在尽可能地类似于您的代码,并且还有很多其他问题,但是在这里解决这些问题会太多,设计更好的代码会更大,并且可能更难以理解-毕竟,这只是意味着告诉你与众不同。

首先让我们定义我们的类(为简单起见,我将字段公开,通常您不这样做):

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 ;
  }
}

然后让我们重申上面的代码:

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 );
});

如您所见,不需要强制转换,并且很明显有哪些可用字段(尽管不一定要填充),这使代码和点错误的推理变得更加容易(编译器可以在这里提供很多帮助,而这是不可能的)在使用地图的示例中为。

答案 1 :(得分:2)

这是一个使用类表示故事和标签的解决方案:

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;
}

用于表示故事的类:

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);
    }
}

用于表示标记的类:

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);
    }
}

输出

[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]

答案 2 :(得分:1)

是的,您可以使用Multimap来实现。首先,我将为Story定义一个pojo,以使情况更清楚:

public class Story {
    private int id;
    private String title;
    private String keyword;
    private String targeting;
//getters setters
}

第二,您需要使用哈希码和等号定义键。

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;
    }

代码如下:

  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())
     );