【问题标题】:Java. How can I check if element was successfully added in set and tracking indexes with forEach?爪哇。如何使用 forEach 检查元素是否已成功添加到集合和跟踪索引中?
【发布时间】:2019-05-27 11:08:13
【问题描述】:

我需要在矩阵中找到不相似的行并返回一组这样的行。

如果这些行中出现的数字集重合,则称该行相似。

示例: 来源:

1 2 2 4 4
4 2 1 4
3 2 4 1 5 8

预期结果:

1 2 2 4 4
3 2 4 1 5 8

我的想法:

通过将二维数组转换为列表来清除每一行中的重复项>

创建新的 int[] 集并添加行,然后如果添加了行,则表示该行不相似。然后记录行数。 return 创建了一组新的原始矩阵行。 我知道我可以通过 Set 的 Add 方法的布尔返回值检查是否添加了元素。 但是 forEach 存在问题,即不提供索引。而且我不能在 forEach 中使用表达式。我该怎么办?

我的代码:

class NonSimilar {
    private int[][] matrix;
    private List<Set<Integer>> rows = new ArrayList<>();

    public NonSimilar (int[][] matrix) {
        this.matrix = matrix;
        for (int i = 0; i < matrix.length; i++) {
            rows.add(Arrays.stream(matrix[i]).boxed().collect(Collectors.toSet()));
        }
    }

    public Set<int[]> getNonSimilarRows() {
        Set<Set<Integer>> nonSimularRows = new HashSet<>();
        rows.forEach(item -> nonSimularRows.add(item));
        // Now I have to check successfully added rows numbers and construct new Set from Origin matrix
        return new HashSet<int[]>();
    }
}

好的。我用 for 迭代替换了 forEach,现在一切正常。

  public Set<int[]> getNonSimilarRows() {
        Set<Set<Integer>> nonSimularRows = new HashSet<>();
        //rows.forEach(item -> nonSimularRows.add(item));
        int index = -1;
        ArrayList<Integer> indexes = new ArrayList<>();
        for (Set<Integer> item : rows) {
            index++;
            if (nonSimularRows.add(item)) {
                indexes.add(index);
            }
        }
        HashSet<int[]> newSet = new HashSet<int[]>();
        for (Integer item : indexes) {
            newSet.add(matrix[item]);
        }
        return newSet;
    }

无论如何,代码看起来都很丑陋,我想获得建议,我可以如何使用现代方法(如 forEach 和 Stream API)重构代码。

【问题讨论】:

  • 为什么预期结果是1 2 2 4 43 2 4 1 5 8 而不是4 2 1 4
  • @Aomine 我相信1 2 2 4 44 2 1 4 在分解为一组时包含相同的元素。两者都是{1, 2, 4}。我假设 OP 想要保留第一个找到的。如果我错了,请纠正我。
  • 因为第一行和第二行相似,但第一行和第三行不相似。我需要得到不相似的行。
  • 请定义行的相似之处。相似度是基于 RoadRunner 所说的吗?
  • 如果这些行中出现的数字集重合,则称该行相似

标签: java algorithm loops foreach set


【解决方案1】:

假设您需要给出现有矩阵的第一个非重复行。然后,您可以使用Map,而不是将索引保存在单独的列表中,其唯一键是一行中的一组数字,值是行本身。这是带有 main 方法的完整类来测试它:

public class NonSimilar {
    private final int[][] matrix;

    public NonSimilar(int[][] matrix) {
        this.matrix = matrix;
    }

    public Set<int[]> getNonSimilarRows() {
        Map<Set<Integer>, int[]> map = new HashMap<>();
        for (int[] row : matrix) {
            map.putIfAbsent(convertRowToSet(row), row);
        }
        return new HashSet<>(map.values());
    }

    public Set<Integer> convertRowToSet(int[] row){
        return Arrays.stream(row).boxed().collect(Collectors.toSet());
    }

    public static void main(String[] args) {
        int[][] matrix = {{1, 2, 2, 4, 4}, {4, 2, 1, 4}, {3, 2, 4, 1, 5, 8}};
        Set<int[]> result = new NonSimilar(matrix).getNonSimilarRows();

        result.forEach(row -> System.out.println(Arrays.toString(row)));
    }
}

现在你可能会说它打印出来了

3 2 4 1 5 8
1 2 2 4 4

而不是

1 2 2 4 4
3 2 4 1 5 8

那是因为结果是Set 并且集合没有顺序的概念。如果您真的希望它以正确的顺序打印,您可以使用 LinkedHashMap 并返回 LinkedHashSet


注意:您甚至可以使用Collectors.toMap 使其更短:

public Set<int[]> getNonSimilarRows() {
    Map<Set<Integer>, int[]> map = Arrays.stream(matrix)
            .collect(Collectors.toMap(this::convertRowToSet, Function.identity(), (r1, r2) -> null));
    return new HashSet<>(map.values());
}

(r1, r2) -&gt; r1 表示您接受重复的键并且您应该保留遇到的第一个值。如果您想保留最后遇到的值,可以将其替换为(r1, r2) -&gt; r2

【讨论】:

    【解决方案2】:

    有了这个

    你可以这样写:

    public class NonSimilarRowsTest {
      @Test
      public void test() {
        int[][] matrix = {{1, 2, 2, 4, 4}, {4, 2, 1, 4}, {3, 2, 4, 1, 5, 8}};
        int[][] expected = {{1, 2, 2, 4, 4}, {3, 2, 4, 1, 5, 8}};
        assertEquals(expected, nonSimilarRows(matrix));
      }
    
      int[][] nonSimilarRows(int[][] matrix) {
        Set<Set<Integer>> rows = new HashSet<>();
        int[][] result = new int[matrix.length][];
        int length = 0;
    
        for (int[] row : matrix) {
          if (rows.add(toSet(row))) {
            result[length++] = row;
          }
        }
    
        return Arrays.copyOf(result, length);
      }
    
      Set<Integer> toSet(int[] array) {
        return Arrays.stream(array).boxed().collect(Collectors.toSet());
      }
    }
    

    【讨论】:

    • 怎么能提前知道预期结果的长度?
    • 这是一个假设。这是一个单元测试,如果发生意外情况,它会失败。
    • 是的,但是你的方法可以返回result吗?
    • 我编辑了我的答案:提取函数nonSimilarRows
    【解决方案3】:

    您只需要 2 行代码即可删除所有“相似”行:

    Set<Set<Integer>> sets = new HashSet<>();
    
    List<int[]> nonSimilar = Arrays.stream(matrix)
        .filter(row -> sets.add(Arrays.stream(row).boxed().collect(Collectors.toSet())))
        .collect(Collectors.toList());
    

    Setadd() 方法返回 true 如果集合已更改 - 即如果要添加的元素尚未在集合中,因此我们可以将其用作过滤器。

    List 被选为流的输出以保持顺序(示例数据似乎暗示了这一要求)。

    我留给读者将List&lt;int[]&gt; 转换为所需的任何输出,因为这对问题/答案并不重要。


    一些测试代码:

    int[][] matrix = {{1, 2, 2, 4, 4},{4, 2, 1, 4}, {3, 2, 4, 1, 5, 8}};
    Set<Set<Integer>> sets = new HashSet<>();
    
    List<int[]> nonSimilar = Arrays.stream(matrix)
        .filter(row -> sets.add(Arrays.stream(row).boxed().collect(Collectors.toSet())))
        .collect(Collectors.toList());
    
    nonSimilar.stream().map(Arrays::toString).forEach(System.out::println);
    

    输出:

    [1, 2, 2, 4, 4]
    [3, 2, 4, 1, 5, 8]
    

    live demo

    【讨论】:

      【解决方案4】:

      这是另一个解决方案,它维护一个 unordered 集合,该集合跟踪重复行并通过将结果存储在列表中来维护顺序:

      import java.util.Set;
      import java.util.HashSet;
      import java.util.List;
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.stream.Collectors;
      
      public class Test {
      
          private static final int[][] rows = new int[][] {
              { 1, 2, 2, 4, 4 },
              { 4, 2, 1, 4 },
              { 3, 2, 4, 1, 5, 8 }
          };
      
          private static Set<Set<Integer>> seenRows = new HashSet<>();
          private static List<int[]> uniqueRows = new ArrayList<>();
      
          public static void main(String[] args) {
              for (int[] row : rows) {
                  Set<Integer> uniqueNumbers = Arrays.stream(row).boxed().collect(Collectors.toSet());
                  if (!seenRows.contains(uniqueNumbers)) {
                      uniqueRows.add(row);
                      seenRows.add(uniqueNumbers);
                  }
              }
      
              for (int[] row : uniqueRows) {
                  System.out.println(Arrays.toString(row));
              }
          }
      }
      

      输出:

      [1, 2, 2, 4, 4]
      [3, 2, 4, 1, 5, 8]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-29
        • 1970-01-01
        • 2014-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-05
        • 1970-01-01
        相关资源
        最近更新 更多