Java 8 - 从列表

时间:2017-01-16 11:28:54

标签: java java-8 java-stream

我有一个要求,我希望使用Java Stream Api处理来自系统的事件流并应用数据清理过程来删除重复的事件。 这是按顺序多次重复删除相同的事件,而不是创建不同事件的列表。大多数在线可用的Java Stream api示例都是从给定输入创建不同的输出。

示例,输入流

  

[a,b,c,a,a,a,a,d,d,d,c,c,e,e,e,e,e,e,f,f,f]

输出列表或流应该是

  

[a,b,c,a,d,c,e,f]

我当前的实现(不使用Stream api)看起来像

public class Test {
    public static void main(String[] args) {
        String fileName = "src/main/resources/test.log";
        try {
            List<String> list = Files.readAllLines(Paths.get(fileName));
            LinkedList<String> acc = new LinkedList<>();

            for (String line: list) {
                if (acc.isEmpty())
                    acc.add(line);
                else if (! line.equals(acc.getLast()) )
                    acc.add(line);
            }

            System.out.println(list);
            System.out.println(acc);

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

输出,

[a, b, c, a, a, a, a, d, d, d, c, c, e, e, e, e, e, e, f, f, f]
[a, b, c, a, d, c, e, f]

我尝试过使用reduce,groupingBy等各种示例,但没有成功。如果存在这样的可能性,我似乎无法找到将流与我的累加器中的最后一个元素进行比较的方法。

5 个答案:

答案 0 :(得分:7)

您可以使用IntStream来掌握List中的索引位置,并按照以下方式使用此优势:

List<String> acc = IntStream
            .range(0, list.size())
            .filter(i -> ((i < list.size() - 1 && !list.get(i).equals(list
                    .get(i + 1))) || i == list.size() - 1))
            .mapToObj(i -> list.get(i)).collect(Collectors.toList());
System.out.println(acc);

<强>解释

  1. IntStream.range(0,list.size()):返回一系列原始的int-valued元素,这些元素将用作访问列表的索引位置。
  2. filter(i -> ((i < list.size() - 1 && !list.get(i).equals(list.get(i + 1) || i == list.size() - 1)):仅当当前索引位置的元素不等于下一个索引位置的元素或者达到最后一个索引位置时才继续
  3. mapToObj(i -> list.get(i):将流转换为Stream<String>
  4. collect(Collectors.toList()):将结果收集到列表中。

答案 1 :(得分:1)

您可以使用自定义Collector来实现目标。请在下面找到详细信息:

Stream<String> lines =  Files.lines(Paths.get("distinct.txt"));
LinkedList<String> values = lines.collect(Collector.of(
            LinkedList::new,
            (list, string) -> {
                if (list.isEmpty())
                    list.add(string);
                else if (!string.equals(list.getLast()))
                    list.add(string);
            },
            (left, right) -> {
                left.addAll(right);
                return left;
            }
    ));

values.forEach(System.out::println);

但是,使用parallel流时可能会遇到一些问题。

答案 2 :(得分:0)

编辑:正如@Bolzano评论的那样,这种方法不符合要求。

如果t是输入流,那么

Map<String,Boolean> s = new HashMap<>();
Stream<String> u = t.filter(e -> s.put(e, Boolean.TRUE)==null);

将生成一个独特元素流,无需创建列表。

然后是一个简单的

List<String> m = u.collect(Collectors.toList());

可以在唯一元素上创建List。

我不明白为什么需要像@CKing和@Anton这样冗长的解决方案?我错过了什么吗?

答案 3 :(得分:0)

对于Java 7,您可以使用迭代器来实现。

Iterator<Integer> iterator = list.values().iterator();
Integer previousValue = null;

while(iterator.hasNext()) {
    Integer currentValue = iterator.next();
    if(currentValue.equals(previousValue)){
        iterator.remove();
    }
    previousValue = currentValue;
}

答案 4 :(得分:-1)

请尝试此解决方案:

public class TestDuplicatePreviousEvent {

public static void main(String[] args) {
    List<Integer> inputData = new ArrayList<>();
    List<Integer> outputData = new ArrayList<>();

    inputData.add(1);
    inputData.add(2);
    inputData.add(2);
    inputData.add(3);
    inputData.add(3);
    inputData.add(3);
    inputData.add(4);
    inputData.add(4);
    inputData.add(4);
    inputData.add(4);
    inputData.add(1);

    AtomicInteger index = new AtomicInteger();
    Map<Integer, Integer> valueByIndex = inputData.stream().collect(Collectors.toMap(i -> index.incrementAndGet(), i -> i));

    outputData = valueByIndex.entrySet().stream().filter(i -> !i.getValue().equals(valueByIndex.get(i.getKey() - 1))).map(x -> x.getValue()).collect(Collectors.toList());
    System.out.println(outputData);
}

}

输出: [1,2,3,4,1]

没有地图的解决方案:

public class TestDuplicatePreviousEvent {

public static void main(String[] args) {
    List<Integer> inputData = new ArrayList<>();
    List<Integer> outputData = new ArrayList<>();

    inputData.add(1);
    inputData.add(2);
    inputData.add(2);
    inputData.add(3);
    inputData.add(3);
    inputData.add(3);
    inputData.add(4);
    inputData.add(4);
    inputData.add(4);
    inputData.add(4);
    inputData.add(1);
    inputData.add(1);
    inputData.add(1);
    inputData.add(4);
    inputData.add(4);

    AtomicInteger index = new AtomicInteger();
    outputData = inputData.stream().filter(i -> filterInputEvents(i, index, inputData)).collect(Collectors.toList());
    System.out.println(outputData);
}

private static boolean filterInputEvents(Integer i, AtomicInteger index, List<Integer> inputData) {

    if (index.get() == 0) {
        index.incrementAndGet();
        return true;
    }
    return !(i.equals(inputData.get(index.getAndIncrement() - 1)));
}

}