2
votes

Rechercher un tableau s'il s'agit d'un sous-ensemble d'un autre tableau

Cette fonction ne doit renvoyer true que si l'objet paramètre est un sous-ensemble de l'objet appelant mais elle renvoie toujours true. Comment y remédier?

    public boolean contains(FileCollection other) {
        int i = 0;
        int j = 0;
        for (i = 0; i<other.files.length; i++) {
            for (j = 0; j<this.files.length; j++) {
                if ((other.files[i]).equals((this.files[j]))) //this refers to the equals method defined in File class
                    break;
            }
            if (j==this.files.length) 
                return false;
        }
        return true;//this method is in FileCollection class
    }


5 commentaires

@vincrichaud c'est très clair - j'ai dit que ça retournait toujours vrai. Mais la réponse ne doit être vraie que si l'objet paramètre est un sous-ensemble de l'objet appelant


Maintenant c'est, merci pour la modification


Votre boucle interne se termine lorsque j = this.files.length + 1


@vincrichaud alors que devrait-il être?


La boucle se termine lorsque la condition ne correspond plus: lorsque j = this.files.length +1 .Dans votre code, vous considérez qu'elle se termine lorsque j = this.files.length . Pour que votre code fonctionne correctement, il vous suffit de changer la condition if en la même que la condition for


5 Réponses :


3
votes

(Puisque vous n'avez pas explicitement exprimé le type de données des éléments du tableau, je suppose que c'est File , déduit des commentaires.)

Si cela ne vous dérange pas de convertir entre les structures de données, peut-être convertir vos tableaux (temporairement) en collections est le moyen le plus simple. Par exemple, conversion en List:

public boolean contains(FileCollection other) {
    Map<File,Integer> otherFrequency = Arrays.stream(other.files)
            .collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));

    Map<File,Integer> thisFrequency  = Arrays.stream(this.files) 
            .collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));

    if (thisFrequency.entrySet().containsAll(otherFrequency).entrySet()) {
        for (File entry : otherFrequency.entrySet()) {
            if (thisFrequency.get(entry) < otherFrequency.get(entry))
                return false;
        }
        return true;
    }
    return false;
}

En fonction de votre clarification de ce qui doit être considéré comme "contient" lorsque les éléments dupliqués sont autorisés, je ' Je dis que vous devez compter le nombre d'existence pour chaque élément. Voici comment:

Sur la base de la réponse de @Eritrean, vous pouvez obtenir et stocker le décompte sur une carte. J'ai également apporté des modifications pour vérifier le décompte:

/* @param other
 * @return true if the calling object contains
 * all files in the parameter object, false otherwise
 */
public boolean contains(FileCollection other) {
    List<File> myList = Arrays.asList(this.files);
    List<File> otherList = Arrays.asList(other.files);
    return myList.containsAll(otherList);
}


15 commentaires

J'écrivais exactement la même suggestion :-)


Remplacez List par List


il retourne toujours faux


@BhushanOza Y a-t-il donc des éléments dupliqués dans chaque tableau?


@renyuneyun oui il peut avoir des éléments en double


@RobertKock Mais nous ne connaîtrons pas le type exact, uniquement indiqué dans le commentaire; p


@BhushanOza Et comment définir "contient" quand this.files a un A tandis que other.files a deux A s? Est-ce vrai ou faux en fonction de vos besoins?


@renyuneyun Les fichiers ont de nombreux attributs comme le nom, la taille, etc. qui doivent être égaux pour que le tableau File soit un sous-ensemble. C'est pourquoi j'ai utilisé ma méthode File.equals () dans la question


@BhushanOza Oui, j'en suis conscient, et les méthodes containsAll () utilisent equals () en interne. Mais le problème n'est pas là. Le problème est de savoir ce que vous définissez comme "contient", lorsque this.files a un élément A tandis que other.files a deux exactement les mêmes A s (de la préoccupation de equals () )? Cela devrait-il être true ou false de votre point de vue?


@renyuneyun qui devrait être faux


@renyuneyun Vous avez raison. J'ai à nouveau modifié la réponse.


@BhushanOza Puisqu'il n'y a toujours pas de réponses ciblant le scénario dupliqué, j'ai ajouté cela à ma réponse. J'espère que ceci résoudra votre problème. Vous aurez besoin du nombre stocké dans une carte, sauf si vous avez trié la liste à l'avance (pour laquelle vous pouvez utiliser rightest_index_of_this_element - leftest_index_of_this_element comme nombre).


@renyuneyun le code Map me donne 2 erreurs: 1. aucune méthode appropriée trouvée pour containsAll (Map ) if (thisFrequency.entrySet (). containsAll (otherFrequency) .entryS‌ et ()) {^ La méthode Collection.containsAll (Collection ) n'est pas applicable (argument incompatible; Map ne peut pas être converti en Collection ) 2. FileCollection.java:16: erreur: incompatible types: l'entrée ne peut pas être convertie en fichier pour (File entry: otherFrequency.entrySet ()) { ^


@renyuneyun J'ai corrigé la première erreur - les crochets étaient mal alignés


@renyuneyun La 2ème erreur a été corrigée en changeant Fichier en Entrée



1
votes

Outre la suggestion @renyuneyun de convertir vos tableaux en listes, vous pouvez également utiliser la méthode String contains

public boolean contains(FileCollection other) {
String myList = Arrays.toString(this.files);
String otherList = Arrays.toString(other.files);
return myList.contains(otherList);
}

Bien sûr, ces deux suggestions ne sont pas les solutions optimales du point de vue de la complexité, mais sont certainement les plus courtes :)


0 commentaires

2
votes

Pour que other.files contienne this.files à contenir, chaque this.file doit être dans other.files code>.

    Comparator<File> comparator = new Comparator<File>() {
        @Override
        public int compare(File lhs, File rhs) {
            int cmp = lhs.getBase().compareIgnoreCase(rhs.getBase());
            if (cmp == 0) {
               cmp = lhs.getExtension().compareIgnoreCase(rhs.getExtension());
            }
            if (cmp == 0) {
               cmp = Long.compare(lhs.getSize(), rhs.getSize());
            }
            return cmp;
        }
    };

    Arrays.sort(this.files, comparator);
    Arrays.sort(other.files, comparator);
    int otherI = 0;
    for (File file : this.files.length) {
        boolean found = false;
        while (otherI < other.files.length) {
            int comparison = comparator.compare(other.files[otherI], file);
            ++otherI;
            if (comparison >= 0) {
                found = comparison == 0;
                break;
            }
        }
        if (!found) { 
            return false;
        }
    }
    return true;

Ne connaissant pas la classe des fichiers , vous pouvez probablement faire:

    for (String file : this.files) {
        boolean found = other.files.indexOf(file) != -1;
        if (!found) { 
            return false;
        }
    }
    return true;

Ou même

    for (String file : this.files) {
        boolean found = false;
        for (String otherFile : other.files) {
            if (otherFile.equals(file)) {
                found = true;
                break;
            }
        }
        if (!found) { 
            return false;
        }
    }
    return true;

Il existe des structures de données plus agréables qui accélèrent les choses et ont des méthodes prédéfinies pour des choses comme contains.


Avec des doublons

    for (int j = 0; j < this.files.length; j++) {
        boolean found = false;
        for (int i = 0; i < other.files.length; i++) {
            if (other.files[i].equals(this.files[j])) {
                found = true;
                break;
            }
        }
        if (!found) { 
            return false;
        }
    }
    return true;

En triant les deux tableaux, vous pouvez synchroniser la comparaison à des emplacements dans les deux tableaux. Ce qui précède gère les doublons.


15 commentaires

En utilisant votre première réponse, l'un des tests échoue - lorsque les deux tableaux ont chacun un double. Ça devrait être vrai mais ça se révèle faux


Dans ce cas, le tri a du sens, puis compter et vérifier les doublons est plus facile. Arrays.sort .


Arrays.sort () ne fonctionne pas. La classe File a de nombreux attributs comme le nom, la taille, etc. Elle donne à ClassCastException


le Arrays.sort () lève ClassCastException


J'ai vu votre commentaire trop tard; a ajouté un comparateur. Pas sûr du fichier de classe, ni du champ à comparer.


La classe File a une méthode equals (). Cela peut être utilisé pour comparer


Malheureusement, il faut également un ordre de tri, <. N'existe-t-il pas un moyen d'avoir le chemin complet sous forme de chaîne ou le nodeid sur Linux?


On pourrait comparer sur tous les attributs, le nom, quand 0, sur la taille, quand 0 sur .... Si vous avez la source de est égal à , vous pouvez voir ce qui rend deux fichiers égaux.


Voici le code de la méthode equals: return (this.getBase (). EqualsIgnoreCase (other.getBase ()) && this.getExtension (). EqualsIgnoreCase (other.getExtension ()) && this.getSize () == autre .getSize () && this.getPermissions () == other.getPermissions ())


Vous pouvez activer cela dans une comparaison imbriquée {-1, 0, 1} en utilisant également getBase (). CompareIgnoreCase (...) . Tant que la comparaison reste à 0, continuez avec l'attribut suivant.


pouvez-vous modifier votre réponse pour l’inclure? comme je ne sais pas comment faire ça


la ligne de comparaison donne cette erreur: '<>' ne peut pas être utilisé avec des classes anonymes


Changé en . Dans le plus récent java, ce ne serait plus une erreur que j'ai supposée.


merci pour l'aide, mais cela échoue toujours à ce test


Merci pour la patience quand même.



1
votes

Qu'en est-il de l'utilisation d'une carte avec Fichier comme clé et fréquence comme valeur:

public boolean contains(FileCollection other) {
    Map<File,Integer> otherFrequency = Arrays.stream(other.files)
            .collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));

    Map<File,Integer> thisFrequency  = Arrays.stream(this.files) 
            .collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));

    return thisFrequency.entrySet().containsAll(otherFrequency.entrySet());
}


0 commentaires

0
votes

Seule cette réponse fonctionne pour moi: (Crédit à @Joop Eggen pour la partie Comparateur)

public boolean contains(FileCollection other) {
    Comparator<File> comparator = new Comparator<File>() {
        @Override
        public int compare(File lhs, File rhs) {
            int cmp = lhs.getBase().compareToIgnoreCase(rhs.getBase());
            if (cmp == 0) {
               cmp = lhs.getExtension().compareToIgnoreCase(rhs.getExtension());
            }
            if (cmp == 0) {
               cmp = Long.compare(lhs.getSize(), rhs.getSize());
            }
            if (cmp == 0) {
               cmp = Long.compare(lhs.getPermissions(), rhs.getPermissions());
            }
            return cmp;
        }
    };
    Arrays.sort(this.files, comparator);
    Arrays.sort(other.files, comparator); //THIS AND THE COMPARATOR SORT THE ARRAYS BASED ON ALL FILE ATTRIBUTES    
    int i = 0;
    int j = 0;
    if (this.files.length<other.files.length)
        return false;
    while (i<other.files.length && j<this.files.length) {
        if (!(this.files[j].equals(other.files[i])))
            j++;
        else {
            j++;
            i++;
        }
    }
    if (i<other.files.length)
        return false;
    else
        return true; 
}


0 commentaires