简体   繁体   English

在特定条件下查找 ArrayList 中的值

[英]Find values in ArrayList with specific conditions

I'm trying to create a function that gives a list of arrays and two arrays of integers as input:我正在尝试创建一个 function ,它给出了 arrays 和两个 arrays 整数列表作为输入:

public boolean[] checkMatching(ArrayList<String[]> items, Integer[] arr1, Integer[] arr2){...} 

checks if there are at least two items that have the same values for the indices of arr1 , but different values for the indices of arr2 .检查是否至少有两个项目对arr1的索引具有相同的值,但对arr2的索引具有不同的值。 For example:例如:

ArrayList<String[]> items = new ArrayList<String[]>();
//All arrays in items have the same number of elements
items.add(new String[]{"B","2","5","6","W"}); //item1
items.add(new String[]{"A","2","5","6","X"}); //item2
items.add(new String[]{"A","2","1","3","Y"}); //item3
items.add(new String[]{"N","F","1","2","W"}); //item4
items.add(new String[]{"A","2","5","6","V"}); //item5

int[] arr1 = new int[] {1,2};
int[] arr2 = new int[] {0,3,4};

boolean[] results = checkMatching(items, arr1, arr2)

In the previous example the function should return:在前面的示例中,function 应该返回:

[-1, 1, -1]

since item1 , item2 , and item5 share the same values for the indices of arr1 , while they have different values for indices 0,4, and equal values for index 3.因为item1item2item5对于arr1的索引具有相同的值,而对于索引 0,4 它们具有不同的值,而对于索引 3 具有相同的值。

Notice that, the method returns 3 integers since in arr2 there are 3 indices.请注意,该方法返回 3 个整数,因为在arr2中有 3 个索引。 Thus, each boolean refers to an index in arr2 .因此,每个 boolean 引用arr2中的索引。

This could be a solution which exploits functional programming:这可能是一个利用函数式编程的解决方案:

public class Temp2 {
    
    public static void main(String[] args) {
        ArrayList<String[]> items = null;

        items = new ArrayList<String[]>();
        items.add(new String[]{"B","2","5","6","W"}); //item1
        items.add(new String[]{"A","2","5","6","X"}); //item2
        items.add(new String[]{"A","2","1","3","Y"}); //item3
        items.add(new String[]{"N","F","1","2","W"}); //item4
        items.add(new String[]{"A","2","5","6","V"}); //item5
        
        int[] arr1 = new int[] {1,2};
        int[] arr2 = new int[] {0,3,4};

        Temp2 tmp = new Temp2();
        int[] results = tmp.checkMatching(items, arr1, arr2);
        
        for(int k = 0; k < 5; k++) {
            System.out.print(results[k]+"|");
        }
        System.out.println();
    }

    public int[] checkMatching(ArrayList<String[]> items, int[] arr, int[] arr2){
        int maxIndex = 4;

        Function<String[], String> compositeKey = el -> getFunctionParametersTest(el, arr);

        Optional<int[]> map = items.stream()
                .collect(Collectors.groupingBy(compositeKey, Collectors.toList())).entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .map(entry -> {
                    List<String[]> values = entry.getValue();
                    int[] generalResponse = new int[maxIndex+1];

                    if (values.size() > 1) {
                        for (int i = 0; i < arr2.length; i++) {
                            String tmp = "";
                            int risposta = -2;
                            for (String[] e : values) {
                                if(tmp.length() > 0) {
                                    if (tmp.compareTo(e[arr2[i]]) != 0) {
                                        risposta = -1;
                                        break;
                                    } else {
                                        risposta = 1;
                                    }
                                }
                                else {
                                    tmp = e[arr2[i]];
                                }
                            }
                            generalResponse[i] = risposta;
                        }
                    }
                    return generalResponse;
                }).reduce((a, b) -> {
                    int[] arrLocal = a;
                    int[] arrarrLocal2 = b;
                    int[] sum = new int[a.length];
                    for (int k = 0; k < arrLocal.length; k++) {
                        if (arrLocal[k] == -1 || arrarrLocal2[k] == -1) {
                            sum[k] = -1;
                        } else if (arrLocal[k] >= arrarrLocal2[k]) {
                            sum[k] = arrLocal[k];
                        } else if (arrLocal[k] < arrarrLocal2[k]) {
                            sum[k] = arrarrLocal2[k];
                        }
                    }
                    return sum;
                });

        int[] finalresults = null;
        if (map.isEmpty() == false) {
            finalresults = map.get();
        } else {
            finalresults = new int[maxIndex+1];
            for (int k = 0; k < maxIndex; k++) {
                finalresults[k] = 1;
            }
        }

        return finalresults;
    }
    
    public String getFunctionParametersTest(String[] item, int[] arr) {
        String values = "";
        for (Integer i : arr) {
            values += item[i] + "-";
        }
        return values;
    }

}

But it is not very efficient, especially when there is a large amount of items.但效率不是很高,尤其是在物品数量很大的情况下。 Is there anyone who can help me?有谁能帮助我吗? I am trying to develop a very performing solution and I don't know if there are faster solutions than functional programming.我正在尝试开发一个性能非常好的解决方案,但我不知道是否有比函数式编程更快的解决方案。

Based on what I understand I came out with the following solution.根据我的理解,我提出了以下解决方案。 However, I am not sure if any list that matches arr1 is what you would want to compare arr2 with.但是,我不确定是否有任何与 arr1 匹配的列表是您想要比较 arr2 的。 You might need to change the findAny() section in case it is different.您可能需要更改findAny()部分以防万一。

public static Boolean[] checkMatching(ArrayList<String[]> items, final int[] arr1, final int[] arr2) {
    // Gets a list containing items that match arr1.
    List<String[]> list = items.stream().collect(Collectors.groupingBy(item ->
            // Groups items together which match in arr1 indexes.
            // Get a stream of indexes contained in arr1.
            Arrays.stream(arr1)
                    // Get a stream of Strings in those indexes.
                    .mapToObj(i -> Arrays.stream(item).collect(Collectors.toList()).get(i))
                    // A list of these Strings is the groupingBy classifier.
                    .collect(Collectors.toList()) 
        )).values().stream().filter(l -> l.size() > 1) // Just get those groups that have more than 1 element.
        .findAny() // Would you want any list that matches arr1?
        .get();

    // Converts arr2 to a Boolean array which says if each index matches all the items in "list".
    Boolean[] matching = Arrays.stream(arr2).mapToObj(i -> list.stream().map(item -> item[i]).distinct().limit(2).count() < 2).toArray(Boolean[]::new);
    return matching;
}

Here's my implementation:这是我的实现:

import java.util.*;
import java.util.function.*;

import static java.util.stream.Collectors.*;

public class Test {
    private static final int LIMIT = 2;
    
    // Implementation ↓
    private static int[] checkMatching(ArrayList<String[]> items,
            int[] keyIndices, int[] valueIndices) {

        Function<String[], String> keyFn = (item) -> Arrays.stream(keyIndices)
                .mapToObj(i -> item[i])
                .collect(joining());

        List<List<String[]>> list = items.stream()
                .collect(groupingBy(keyFn, toList())).values().stream()
                .filter(e -> e.size() >= LIMIT)
                .collect(toList());

        return Arrays.stream(valueIndices)
                .map(i -> list.stream()
                        .anyMatch(lists -> lists.stream()
                                .map(item -> item[i])
                                .collect(toSet()).size() == 1) ? 1 : -1)
                .toArray();
    }

    // Test code ↓
    public static void main(String[] args) {
        ArrayList<String[]> items = new ArrayList<>();
        items.add(new String[] {"B", "2", "5", "6", "W"});
        items.add(new String[] {"A", "2", "5", "6", "X"});
        items.add(new String[] {"A", "2", "1", "3", "Y"});
        items.add(new String[] {"N", "F", "1", "2", "W"});
        items.add(new String[] {"A", "2", "5", "6", "V"});

        int[] arr1 = new int[] {1, 2};
        int[] arr2 = new int[] {0, 3, 4};
        int[] results = checkMatching(items, arr1, arr2);

        System.out.println(Arrays.toString(results));
    }
}

Which generates the following output:生成以下 output:

[-1, 1, -1]

My approach is as follows:我的方法如下:

  • build up a map with lists of string arrays for which the indices of arr1 match;arr1的索引匹配的字符串 arrays 列表建立一个 map;
  • filter out the lists that don't have at least two elements;过滤掉没有至少两个元素的列表;
  • map arr2 to a list of booleans to represent whether all the values are the same for at least one of the entries in the map (using a Set to check for distinct values). map arr2到布尔值列表,以表示 map 中至少一个条目的所有值是否相同(使用Set检查不同的值)。

The last step is open to optimization.最后一步可以优化。 Instead of counting the number of distinct values, you could return early from the moment one different value is found, but it would lead to slightly less elegant and less functional code:您可以从找到一个不同值的那一刻起尽早返回,而不是计算不同值的数量,但这会导致代码稍微不那么优雅和功能更少:

return Arrays.stream(valueIndices)
        .map(i -> list.stream()
                .anyMatch(arrays -> {
                    for (int j = 1; j < arrays.size(); j++) {
                        if (!arrays.get(j)[i].equals(arrays.get(j - 1)[i])) {
                            return false;
                        }
                    }
                    return true;
                }) ? 1 : -1)
        .toArray();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM