【问题标题】:java 8 use reduce and Collectors grouping by to get listjava 8使用reduce和Collectors grouping by来获取列表
【发布时间】:2017-05-13 14:13:48
【问题描述】:

编辑 **请求提供第一种方法的答案也使用reduce方法**

 public class Messages {
  int id;
  String message;
  String field1;
  String field2;
  String field3;
  int audId;
  String audmessage;
 //constructor
   //getter or setters
 }


public class CustomMessage {

 int id;
 String msg;
 String field1;
 String field2;
 String field3;
 List<Aud> list;
 //getters and setters
}


  public class Aud {

    int id;
    String message;
   //getters and setters
   }


   public class Demo {

    public static  void main(String args[]){
      List<Messages> list = new ArrayList<Messages>();
      list.add(new Messages(1,"abc","c","d","f",10,"a1"));
      list.add(new Messages(2,"ac","d","d","f",21,"a2"));
      list.add(new Messages(3,"adc","s","d","f",31,"a3"));
      list.add(new Messages(4,"aec","g","d","f",40,"a4"));
      list.add(new Messages(1,"abc","c","d","f",11,"a5"));
      list.add(new Messages(2,"ac","d","d","f",22,"a5"));
     }

我希望将消息与审核进行映射 CustomMessage 必须有 ->1,"abc","c","d","f"----->2 个审计列表 (10,a1) 和 (11,"a5");

有两种方法

1.Reduce-我也想用reduce来创建我自己的累加器和合并器

   List<CustomMessage> list1= list.stream().reduce(new ArrayList<CustomMessage>(),
            accumulator1,
            combiner1);

    **I am unable to write a accumulator and combiner**

2.Collectors.groupingBy-

  • 我不想使用构造函数来创建 Message自定义消息也不是。这里我的字段较少,我的实际对象有很多字段。任何方式都有一个静态 对象创建方法
  • 有没有办法通过编写累加器或减少来做到这一点 合路器

          List<CustomMessage> l = list.stream()
            .collect(Collectors.groupingBy(m -> new SimpleEntry<>(m.getId(), m.getMessage()),
                    Collectors.mapping(m -> new Aud(m.getAudId(), m.getAudMessage()), Collectors.toList())))
            .entrySet()
            .stream()
            .map(e -> new CustomMessage(e.getKey().getKey(), e.getKey().getValue(), e.getValue()))
            .collect(Collectors.toList());
    

谁能帮我解决这两种方法。

【问题讨论】:

  • 如果您有类似new Messages(1,"abc","d","c","f",10,"a1")); 的消息以及您拥有的消息会发生什么
  • 我的场景不会发生这种情况
  • 对于groupingBy() 方法,您的Message 类是否定义了equals()(和hashcode())?
  • 我假设Aud 中的messageString
  • @OleV.V.是的,它有等于和哈希码。它在 id 字段上

标签: java-8 java-stream


【解决方案1】:

此代码将创建 CollectionCustomMessage。我建议在 CustomMessage 中放置一个带有 Messages 参数的构造函数。并且也许还将mergeFunction移出collect

Collection<CustomMessage> customMessages = list.stream()
        .collect(toMap(
                Messages::getId,
                m -> new CustomMessage(m.getId(), m.getMessage(), m.getField1(), m.getField2(), m.getField3(),
                        new ArrayList<>(singletonList(new Aud(m.getAudId(), m.getAudmessage())))),
                (m1, m2) -> {
                    m1.getList().addAll(m2.getList());
                    return m1;
                }))
        .values();

toMap 在这里所做的是:第一次遇到 Messages id 时,它会将其作为键放入 Map 中,其值是新创建的 CustomMessage 通过 toMap 的第二个参数( “valueMapper”)。下一次它将合并两个 CustomMessage 和第三个参数 "mergeFunction",这将有效地连接两个 Aud 列表。

如果您绝对需要List 而不是Collection

List<CustomMessage> lm = new ArrayList<>(customMessages);

【讨论】:

  • 当你回答时我想我更新了我的问题。你能检查我更新的问题吗
  • 不错。我们给提问者一些选择。并恭喜您获得了 10k。
  • 我对你的代码有一些疑问。singleton list 它在这里使用了什么,你能解释一下关于合并部分的更多信息
  • Collections.singletonList :返回一个只包含指定对象的不可变列表。然后我创建一个new ArrayList 以便能够添加元素。或者,如果您可以使用 Guava,则可以使用 Lists.newArrayList
  • 合并取2个CustomMessage对象m1m2,将m2中列表中的所有Aud对象复制到m1中的对象并返回m1m2 被丢弃。
【解决方案2】:

您不能通过要么 分组 减少来做到这一点。你需要两者:先分组,然后减少。我对减少进行了不同的编码:

    List<CustomMessage> list1 = list.stream()
            .collect(Collectors.groupingBy(Messages::getId))
            .values()
            .stream() // stream of List<Messages>
            .map(lm -> {
                List<Aud> la = lm.stream()
                        .map(m -> new Aud(m.getAudId(), m.getAudmessage()))
                        .collect(Collectors.toList());
                Messages m0 = lm.get(0);
                return new CustomMessage(m0.getId(), m0.getMessage(), 
                        m0.getField1(), m0.getField2(), m0.getField3(), la);
            })
            .collect(Collectors.toList());

我在Aud 中引入了一个构造函数,然后阅读了您试图避免构造函数的评论。最后我会回到这一点。无论如何,您可以将 Aud 对象的创建重写为与您的问题相同的方式。如果您愿意,也可以构造CustomMessage 对象。

结果:

[1 abc c d f [10 a1, 11 a5], 3 adc s d f [31 a3], 4 aec g d f [40 a4], 2 ac d d f [21 a2, 22 a5]]

我仅按 ID 对消息进行分组,因为您说他们的 equals 方法仅使用 ID。您还可以按问题中的更多字段进行分组。一种快速而肮脏的方式将是

            .collect(Collectors.groupingBy(m -> "" + m.getId() + '-' + m.getMessage() 
                    + '-' + m.getField1() + '-' + m.getField2() + '-' + m.getField3()))

避免使用公共构造函数并使用静态方法创建对象并没有太大变化。例如,如果您有

public static Aud createAud(int id, String message) {
    return new Aud(id, message);
}

(好吧,这并没有完全消除构造函数,但现在您可以将其声明为私有;如果仍然不满意,您还可以将方法重写为不使用已声明的构造函数)。现在在流中你只需要做:

                        .map(m -> Aud.createAud(m.getAudId(), m.getAudmessage()))

您可以对CustomMessage 执行类似操作。在这种情况下,如果您愿意,您的静态方法可以采用 Messages 参数,有点像 Manos Nikolaidis 建议的那样,这可以稍微简化流代码。

编辑:你不能忘记三参数reduce 方法,对吗? ;-) 它可以使用。如果您想这样做,我建议您首先为CustomMessage 提供一系列方法:

private CustomMessage(int id, String msg, 
                      String field1, String field2, String field3, List<Aud> list) {
    this.id = id;
    this.msg = msg;
    this.field1 = field1;
    this.field2 = field2;
    this.field3 = field3;
    this.list = list;
}

public static CustomMessage create(Messages m, List<Aud> la) {
    return new CustomMessage(m.getId(), m.getMessage(), 
                             m.getField1(), m.getField2(), m.getField3(), la);
}

/**
 * @return original with the Aud from m added
 */
public static CustomMessage adopt(CustomMessage original, Messages m) {
    if (original.getId() != m.getId()) {
        throw new IllegalArgumentException("adopt(): incompatible messages, wrong ID");
    }
    Aud newAud = Aud.createAud(m.getAudId(), m.getAudmessage());
    original.addAud(newAud);
    return original;
}

public static CustomMessage merge(CustomMessage cm1, CustomMessage cm2) {
    if (cm1.getId() != cm2.getId()) {
        throw new IllegalArgumentException("Cannot merge non-matching custom messages, id "
                + cm1.getId() + " and " + cm2.getId());
    }
    cm1.addAuds(cm2.getList());
    return cm1;
}

private void addAud(Aud aud) {
    list.add(aud);
}

private void addAuds(List<Aud> list) {
    this.list.addAll(list);
}

有了这些,情况还不错:

    List<CustomMessage> list2 = list.stream()
            .collect(Collectors.groupingBy(Messages::getId))
            .values()
            .stream()
            .map(lm -> lm.stream()
                        .reduce(CustomMessage.create(lm.get(0), new ArrayList<>()),
                                CustomMessage::adopt, 
                                CustomMessage::merge))
            .collect(Collectors.toList());

【讨论】:

  • 因为我想有一个静态方法来创建对象,因为这些是复杂的对象,有些正在扩展它们......所以我想编写自己的静态方法来创建对象。我不想介绍构造函数。请建议。是否可以使用带有累加器或组合器的 reduce 函数编写。
  • 已编辑,尽量满足您的愿望。
  • 非常感谢。它可以工作,但如果可能的话,我想了解 List list1= list.stream().reduce(new ArrayList() 是否可以编写相同的代码,累加器1,组合器1)。
  • 等你回复
  • 已解决。不过,花了相当多的代码行。 @coder25
猜你喜欢
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 2022-12-02
  • 2019-11-24
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多