从两个列表中查找公共元素(使用PhoneNumberUtils.compare)

时间:2014-09-02 11:22:40

标签: java android algorithm

所以在这个Android应用程序中,我有两个带有电话号码的列表。我想找出两者中的常见数字。蛮力方式是N ^ 2。我不能使用HashSets(我猜),因为数字可以采用不同的格式。所以我最好的选择是使用PhoneNumberUtils.compare。 (它匹配不同格式的数字。例如,对于" +91 9413294132"以及" 09413294132"返回true;

我想要一种有效的方式,因为列表太大了。一个是我的数据库,另一个是用户的联系人。所以基本上我想知道在我的数据库中注册了哪些号码。 (也许WhatsApp发现谁在WhatsApp上的方式相同)。我真的很感激任何努力。感谢。

1 个答案:

答案 0 :(得分:1)

这取决于你是否关心记忆/时间或两者。

  1. 你关心记忆

    使用强力N 2 解决方案,因为它根本不使用额外的内存(只保留公共元素所需的内存),而N 2 在实践中对于小尺寸列表来说并不是那么糟糕。无论如何,从数据库中获取电话号码更有可能导致比N 2 算法更大的性能损失。

  2. 你关心时间,但你可以使用额外的记忆

    将您的电话号码包装到包装类中,并将第一个列表中的所有电话号码添加到HashSet。然后,遍历您的第二个列表并检查set是否包含已包装的电话号码。这将保证你有一个O(m + k * n)时间(你必须只迭代每个列表一次,而HastSet的contains方法是一个常数k - 其中k表示具有相同hashCode的字符串的平均数量。)。这倾向于O(n)因为2和k(应该是1,因为String的hashSet()碰撞非常罕见)是可以被丢弃的常数因子。

    class PhoneNumber { 
    
         private final String val;
    
         public PhoneNumber(final String val){
             this.val = val;
         }
    
         @Override
         public int hashCode(){
             return this.getVal().hashCode();
         }
    
         @Override
         public boolean equals(Object obj){
             if (this == obj) {
               return true;
             }
             if (obj == null) {
               return false;
             }
             if (this.getClass() != obj.getClass()) {
               return false;
             }
    
             PhoneNumber other = (PhoneNumber )obj;
             return PhoneNumberUtils.compare(this.getVal(), other.getVal()) == 0;
         }
    
         public String getVal(){
            return this.val;
         }
    } 
    
    private Set<PhoneNumber> getCommonPhoneNumbers(List<String> dbPhoneNumbers , List<String> userPhoneNumbers){
        Set<PhoneNumber> common = new HashSet<PhoneNumber>();
    
        Set<PhoneNumber> phoneNumbers = new HashSet<PhoneNumber>();
        for(String s : userPhoneNumbers){
            phoneNumbers.add(new PhoneNumber(s));
        }
    
        for(String s : dbPhoneNumbers ){
            PhoneNumber phoneNo = new PhoneNumber(s);
            if(phoneNumbers.contains(phoneNo)){
                common.add(phoneNo);
            }
        }
    
        return common;
    }
    
  3. 你关注内存和时间的复杂性。

    使用基于PhoneNumberUtils.compare(String,String)的自定义比较器对2个列表进行排序(这应该采用O(n log n)),然后一次迭代两个列表(O(min(m,n)) ):

      private static final Comparator<String> phoneNoComp = new Comparator<String>(){
          @Override
          public int compare(final String s1, final String s2) {
            return PhoneNumberUtils.compare(s1,s2);
          }
      };
    
      private Set<String> getCommonPhoneNumbers(final List<String> list1 , final List<String> list2){
    
          Set<String> common = new HashSet<String>();
    
          Collections.sort(list1, phoneNoComp);
          Collections.sort(list2, phoneNoComp);
    
          int size1 = list1.size();
          int size2 = list2.size();
          int i = 0, j = 0;
    
          while(i < size1 && j < size2){
              int comparison = PhoneNumberUtils.compare(list1.get(i) , list2.get(j));
              if( comparison == 0) { // found a common element.
                  common.add(list1.get(i));
                  i++;
                  j++;
              }
              else if(comparison == 1){
                  j++;
              }
              else{
                  i++;
              }
          }
    
          return common;
      }