使用Java流

时间:2018-01-29 18:37:50

标签: java arraylist

我想使用 Java流ArrayList<String>转换为Set<ScopeItem>

ScopeItem is a enum;
items is an ArrayList<String>;

Set<ScopeItem> scopeItems = items.stream()
                    .map(scopeString -> ScopeItem.valueOf(scopeString))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

在枚举中没有的字符串中会抛出以下内容:

java.lang.IllegalArgumentException: No enum const...

理想情况下,我想跳过任何不匹配的字符串。

我想也许是使用flatmap?任何想法怎么做?

6 个答案:

答案 0 :(得分:3)

您可以将以下方法添加到ScopeItem

public static ScopeItem valueOfOrNull(String name) {
    try {
        return valueOf(name);
    } catch (IllegalArgumentException e) {
        // no such element
        return null;
    }
}

并使用它来映射枚举值:

.map(scopeString -> ScopeItem.valueOfOrNull(scopeString))

非空值(您已经拥有)的后续.filter()将过滤掉与不匹配字符串对应的空值。

答案 1 :(得分:1)

您可以在map内添加try-catch以返回null,而不是抛出异常:

Set<ScopeItem> scopeItems = items.stream()
    .map(scopeString ->
        {
           try
           {
              return ScopeItem.valueOf(scopeString);
           }
           catch (IllegalArgumentException e)
           {
              return null;
           }
        })
    .filter(Objects::nonNull)
    .collect(Collectors.toSet());

您也可以事先使用filter来检查值数组是否包含您要查找的字符串:

Set<ScopeItem> scopeItems = items.stream()
    .filter(scopeString -> Arrays.stream(ScopeItem.values())
                               .anyMatch(scopeItem -> scopeItem.name().equals(scopeString)))
    .map(ScopeItem::valueOf)
    .collect(Collectors.toSet());

答案 2 :(得分:1)

与其他人不同,我不推荐使用例外,因为我觉得它们应该用于特殊情况,而不是用于可能发生的事情。一个简单的解决方案是使用一组可接受的字符串静态集合,并简单地检查一下你要使用valueOf的字符串是否在所述集合中。

package test;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Test {

    public static enum ScopeItem {
        ScopeA,
        ScopeB;

        private static Set<String> castableStrings;

        static {
            castableStrings = new HashSet<>();
            for (ScopeItem i : ScopeItem.values()) {
                castableStrings.add(i.name());
            }
        }

        public static boolean acceptable(String s) {
            return castableStrings.contains(s);
        }
    }

    public static void main(String[] args) {

       List<String> items = Arrays.asList("ScopeA", "RandomString", "ScopeB");
       Set<ScopeItem> scopeItems = items.stream()
               .filter(ScopeItem::acceptable)
               .map(ScopeItem::valueOf)
               .collect(Collectors.toSet());

       System.out.println(scopeItems.size());
    }

}

答案 3 :(得分:1)

这里有一种更优雅的方法。您不必为枚举添加任何新字段;你也可以简单地对 it 运行一个流,并确定你的收藏中是否有任何匹配。

以下代码假定枚举声明为:

enum F {
    A, B, C, D, E
}

并且看起来如此:

List<String> bad = Arrays.asList("A", "a", "B", "b", "C", "c");

final Set<F> collect = bad.stream()
                                .filter(e -> Arrays.stream(F.values())
                                                     .map(F::name)
                                                     .anyMatch(m -> Objects.equals(e, m)))
                                .map(F::valueOf)
                                .collect(Collectors.toSet());

这里要注意两个部分:

  • 我们对枚举值进行内部过滤,并通过F::name将其映射到字符串。
  • 我们确定我们的基本集合的元素是否与null安全方式的枚举元素匹配(Objects.equals在这里使用空值做正确的事情)

答案 4 :(得分:0)

您可以使用Apache Common's commons-lang3 EnumUtils.getEnum()代替gcc -Wall -std=gnu99 fileTest.c -o fileTest。如果没有匹配的枚举条目(然后您可以在代码中完全过滤),则返回null。

答案 5 :(得分:0)

最简单,最清晰的方法是在values()上过滤您的枚举items.contains()方法:

Set<ScopeItem> enumVals = Arrays.stream(ScopeItem.values())
        .filter(e -> items.contains(e.name()))
        .collect(Collectors.toSet());

没有添加任何功能只是为了让流做你想做的事情,而且这一目了然很明显。