使用Stream和Lambda对Java对象进行多个操作

3

我有一个有效构造函数的class A {int sNo; String name;}。 我需要使用流API对名称进行修剪并按名称排序。

public class Test1 {
    public static void main(String[] args) {



    ArrayList<A> l = new ArrayList();
    l.add(new A(1, " 1name "));
    l.add(new A(7, " 3name "));
    l.add(new A(6, ""));
    l.add(new A(5, " 2name "));
    l.add(new A(4, " 5name "));
    l.add(new A(2, ""));

    List<String> i = l.stream()
            .filter(s -> !s.name.isEmpty())
            .map(s -> s.name.trim())
            .sorted()
            .collect(Collectors.toList());
    System.out.println(i); 
}

它只返回按名称排序的结果

[, 1name, 2name, 3name, 5name]

但我需要整个对象。

我知道s -> s.name.trim()是它仅存储名称的原因,

如何在特定字段上应用操作,但存储整个对象。


谢谢@janos的回复,是的它工作得很好,但我有一个小疑问。 s->new A(s.sNo, s.name.trim())) 不具有可扩展性,我的意思是在这个示例中类A只包含2个变量,所以调用其构造函数是可以接受的,但如果类A有20-30个变量,那么使用所有相同的值调用构造函数并仅更新1个变量是不明智的,请问是否有解决方法? - shrikant.sharma
3个回答

2

仅仅是为了好玩,并不是因为它特别好或者被推荐。也可以使用匿名对象来实现:

    List<A> i = l.stream()
            .filter(s -> !s.name.isEmpty())
            .map(s -> new Object() {A a = s; String name = s.name.trim();})
            .sorted(Comparator.comparing(o -> o.name))
            .map(o -> o.a)
            .collect(Collectors.toList());

并不是每个编译器都能接受这种写法(javac可以),但这是合法的Java代码。

1
当您使用map函数时,您正在将所有对象转换为字符串,因此请改用for each来转换对象内部的字符串,但不要返回字符串列表:
 class A {
 int sNo; 
 String name;

 public int getName(){ return name}



 public void setName(String name){ this.name = name;}

  }

i.forEach(s -> s.setName(s.getName().trim()));

List<A> i = l.stream()
        .filter(s -> !s.name.isEmpty())       
        .sorted(Comparator.comparing(a -> a.name))
        .collect(Collectors.toList());

除了占用 CPU 之外,你认为 forEach(s -> s.name.trim()) 做了什么? - Andreas
@Andreas 你是对的,我忘记设置更新对象状态的值了。 - developer_hatch
s 没有 setName() 方法。如果有的话,很可能也有一个 getName() 方法。 - Andreas
@Andreas 当然当然,忘记添加那个重要的细节了,我又更新了。非常感谢。 - developer_hatch

1
它仅按排序顺序返回名称[...],但我需要整个对象。 步骤.map(s -> s.name.trim())将您的A流转换为String流。这就是为什么您只剩下字符串而不是整个对象的原因。
如果您想要一个对象的排序列表,则结果应该在List<A>中,而不是List<String>中。
要按字段对对象列表进行排序,可以将Comparator传递给sorted
List<A> result = l.stream()
    .filter(s -> !s.name.isEmpty())
    .map(s -> new A(s.sNo, s.name.trim()))
    .sorted(Comparator.comparing(a -> a.name))
    .collect(Collectors.toList());

如果您不想使用修剪后的名称创建一个new A,而是想修改原始对象(尽管我严重怀疑这一点),您可以用peek替换map步骤并修改对象:
.peek(s -> s.name = s.name.trim())

然而,需要注意的是,正如@Mustafa指出的那样,peek仅用于调试。实际上,如果您想修改底层对象,最好将其与过滤和排序分开,在逻辑上它们是不相关的步骤(看起来像数据清理)。可以使用forEach来实现该步骤。
如果您实际上不需要修剪名称(感谢@Andreas的提示),仅用于排序目的,则可以完全省略map步骤,并像这样编写sorted步骤:
.sorted(Comparator.comparing(a -> a.name.trim()))

你可以直接使用 sorted(Comparator.comparing(a -> a.name.trim()))。当然,这样做对性能不利,但它能够工作并消除了对 map() 的需求。 - Andreas
@Andreas 我的印象是 OP 实际上想要修剪字符串,而不仅仅是在修剪后的值上进行排序。但你的想法更有可能,谢谢,我更新了我的答案。 - janos
创建新的对象更好,否则建议使用forEach而不是peek。根据文档peek仅用于调试。 - Mustafa
感谢@janos的回复,是的它运行良好,但我有一个小疑问。 s->new A(s.sNo, s.name.trim())不具有可扩展性,我的意思是在这个例子中,类A只包含2个变量,所以可以调用其构造函数,但是如果类A有20-30个变量,那么就不明智了,因为需要输入所有相同的值并且我只更改了1个。是否有任何解决方法? - shrikant.sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接