【问题标题】:How to only return ArrayList of strings with minimum number of occurrences?如何仅返回出现次数最少的字符串的 ArrayList?
【发布时间】:2015-07-11 22:00:26
【问题描述】:

我有一个String[]originalStringArray,其中有重复项。所以{"dog","cat","dog","fish","dog","cat"}

我想创建一个函数,它只返回恰好出现一定次数的字符串。在这里,如果我说 3,它将返回“狗”而不是“猫”。

这是我当前的代码:

public ArrayList<String>  returnMultiples(String[] originalStringArray,int requiredCount){
    ArrayList<Integer> mCount = new ArrayList<>();
    List<String> list = Arrays.asList(originalStringArray);
    ArrayList<String> result = new ArrayList<>();

    // Count occurrences in original string
    for(String item: originalStringArray){
        mCount.add(Collections.frequency(list,item));
    }

    // If frequency is equal to count, add to array list
    for(int i=0; i<mCount.size(); i++){
        if(mCount.get(i) == requiredCount){
            result.add(originalStringArray[i]);
        }
    }

    return result;
}

我遇到的问题是,我在某处读到 Collections 库非常缓慢且拖累,而且似乎可以使用 HashSets 和表来减少这种方法。不幸的是,我对如何做到这一点有点茫然。有没有更好的方法来做到这一点?

【问题讨论】:

  • 显示引文。 Java Collections 库是高度优化的。说他们很慢的人通常没有正确使用它们。你是对的。你想要一个 Map 来解决这个问题。特别是,如果你想保持原来的出现顺序,请使用 OrderedHashMap。
  • 在我看来,如果您要处理少量数据,性能并不重要。当您处理数千个或更多元素时,性能开始变得重要,即使在这个数量上也只是一点点。话虽如此,您不能使用集合,因为每个集合元素都必须是唯一的。在初始数组循环期间,我会将每个元素插入到哈希图中。然后,您需要遍历 hashmap 并获取与您的出现条件匹配的键。
  • Multiset 来自Guava library 正是为此目的而设计的。
  • @Gene 好吧,坦率地说,引用将是对另一个 SO 问题的评论。不是一个非常有效的来源。感谢 OrderHashMap 的想法。

标签: java android arraylist


【解决方案1】:

需要某种地图来执行此操作。这是一个使用 HashMaps 编写的示例:

public ArrayList<String> returnMultiples(String[] array, int min){
    HashMap<String, Integer> counts = new HashMap<String, Integer>();//instantiate a new HashMap

    //loop through the array and count the occurrences of each different string in the array
    for(int i = 0; i < array.length; i++){
        String word = array[i];
        if(counts.containsKey(word))
            counts.put(word, counts.get(word) + 1);
        else
            counts.put(word, 1);
    }

    ArrayList<String> multiples = new ArrayList<String>();

    //check if any of the words occur >= min times. if so, add them to the returning list.
    for(String key : counts.keySet()){
        if(counts.get(key) >= min){
            multiples.add(key);
        }
    }

    return multiples;//return the list we just created of the desired strings
}

根据字符串的长度,HashMap 将比使用集合更高效一些,尽管差异几乎可以忽略不计。

【讨论】:

  • @sparkysword 没问题 - 我很高兴能帮上忙。
  • 而不是在第一个for 循环中使用条件您可以使用HashMapgetOrDefault 方法,如下所示。
【解决方案2】:

您必须使用HashMap 来完成此任务。

假设您的HashMap 将包含给定字符串的出现次数,因此它的类型为HasMap&lt;String,Integer&gt;

现在,让我们遍历您的集合:

  1. 从您的收藏中获取另一个字符串
  2. 检查 HashMap 中是否存在给定字符串 (#contains)
  3. 如果不存在,则使用 String 键 (hashMap.put(stringKey,1); 放入新元素;
  4. 如果存在,则放入具有相同键的元素,但增加内部计数器 (hashMap.put(stringKey,hashMap.get(stringKey)+1)
  5. 继续

现在您的 hashmap 包含了集合中给定字符串的确切出现次数。

快速查找将创建反向HashMap&lt;Integer,String&gt;,但计数可能会重复,这将不起作用。要获得出现次数与给定字符串匹配的字符串,您必须遍历 map 的所有键,并仅返回出现次数与您的条件匹配的那些。

【讨论】:

    【解决方案3】:

    您的算法将返回重复项。

    HashSet Collections 库的一部分,所以对你没有任何好处。

    包含 Collections.frequency 的循环是一个 O(n^2) 算法。 (对于 originalStringArray Collections.frequency 中的每个 String 再次循环整个 originalStringArray)。

    你可以只用一个 HashMap 来做到这一点。

    在映射中为 originalStringArray 中的每个字符串增加一个整数。

    删除所有值与 requiredCount 不同的键。

    如果您确实打算返回一个 ArrayList,请将 map.keySet() 添加到一个新的 ArrayList。

    如果你想要一个数组,也可以使用 map.keySet().toArray(String[map.size()])。

    【讨论】:

      【解决方案4】:

      您可以使用AVL Tree,前提是如果您的数组中有 1,000,000 个项目,则需要1,000,000 steps 才能通过该数据结构。使用AVL Tree 需要O(Log (1,000,000)) 步骤,即== 6 步骤,非常整洁。如果您的数据是动态的,这将是一个很好的方法,尽管您必须优化插入。

      使用 AVL 树,所有内容都会被排序,因此您会得到 O(Log N) 时间。而不是像 N Steps 那样遍历数组:

      你可以有这样的东西:

      它检查根并发现Char c 大于dog 中的第一个Char,并横向向左。基本上通过1/2 每一步将搜索时间缩短为O(Log N) 步骤。你必须保持树的高度平衡。

      AVL Tree 的好处在于您的数据始终处于排序状态,因为树需要平衡。

      如果数据不经常更改并且您不需要排序数据,那么使用HashMap 可能会更好。

      【讨论】:

      • 哇,感谢您提供的详细信息。我听说过很多不同的树,但以前从未听说过 AVL。
      【解决方案5】:

      我想,使用哈希映射就足够高效了。

      我想到的最短代码(并且使用 HashMaps)如下所示:

      String[] filter(String[] collection, int requirement) {
          final HashMap<String, Integer> temp = new HashMap<>();
      
          for (String item : collection) {
              int current = temp.getOrDefault(item, 0);
              temp.put(item, ++current);
          }
      
          final Iterator<Entry<String, Integer>> iterator = temp.entrySet().iterator();
          while (iterator.hasNext()) {
              final Entry<String, Integer> entry = iterator.next();
              if (entry.getValue() != requirement) {
                  iterator.remove();
              }
          }
      
          return temp.keySet().toArray(new String[temp.size()]);
      }
      

      可以如下使用:

      final String[] array = new String[]{
          "dog", "dog", "dog", "cat", "cat", "fish", "cat"
      };
      
      final String[] result = filter(array, 3);
      
      for (String item : result) {
          System.out.println(item);
      }
      

      并按预期生成输出:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-10
        • 1970-01-01
        • 2013-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多