Каждый с каждым

Допустим есть класс

class Team {

}

и мне нужно каким-либо образом обыграть это так, чтобы каждая команда из имеющегося у меня List<Team> (команда A, команда B, команда C, команда D) сражалась против другой команды (то есть A играют с B, потом с C, пока B играют с D), но при этом они не повторялись. Как правильно реализовать это в Java?


Ответы (3 шт):

Автор решения: had0uken

Не уверен как правильно, может есть более легкий способ. Но сразу в голову приходит воспользоваться коллекцией hashmap. Потому что это всегда очень быстрое и довольно универсальное решение. Код с объяснениями ниже:

public class Test {
    public static void main(String[] args) {
        List<Team> list = new ArrayList<>();
        list.add(new Team("A"));
        list.add(new Team("B"));
        list.add(new Team("C"));
        list.add(new Team("D"));
        list.add(new Team("G"));
        play(list);


    }
    public static void play(List<Team>list){
        Map<Team, List<Team>> alreadyPlayed = new HashMap<>();
        //инициализация хэшмепа: команда: список команд, с которыми уже сыграла данная команда
        for(int i=0;i<list.size();i++)
            alreadyPlayed.put(list.get(i), new ArrayList<>());

        for(int i=0;i<list.size();i++)
            for(int j=0;j<list.size();j++)
            {
                if
                ((!list.get(i).equals(list.get(j)))             // проверка чтобы команда А не играла с командой А
                &&
                (!alreadyPlayed.get(list.get(i)).contains(list.get(j))) // проверка что команды еще не играли между собой
                )
            {
                    alreadyPlayed.get(list.get(i)).add(list.get(j));
                    alreadyPlayed.get(list.get(j)).add(list.get(i));
                    //добавление в хэшмеп инофрмации что команды между собой сыграли
                    System.out.println(list.get(i)+" plays with "+list.get(j));
                    //собственно игра
            }


    }
}
}

class Team {
    String name;

    public Team(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Team " + name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Team team = (Team) o;
        return Objects.equals(name, team.name);
    }
}

Вывод консоль:

Team A plays with Team B
Team A plays with Team C
Team A plays with Team D
Team A plays with Team G
Team B plays with Team C
Team B plays with Team D
Team B plays with Team G
Team C plays with Team D
Team C plays with Team G
Team D plays with Team G

Process finished with exit code 0
→ Ссылка
Автор решения: kami

Чтобы игры не повторялись - логично воспользоваться коллекцией, которая самостоятельно отсекает повторения: Set

Собственно, "игра" в контексте задачи - это комбинация 2 команд. Единственное, что требуется - это обеспечить уникальность вне зависимости от расположения этих команд в игре. Для этого (дабы Set мог правильно отсекать дубликаты) следует перекрыть базовые методы equals() и hashCode():

    private record Team(String name) {
    }

    private record Game(Team team1, Team team2) {
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Game game = (Game) o;
            return (team1.equals(game.team1) && team2.equals(game.team2))
                    || (team1.equals(game.team2) && team2.equals(game.team1));
        }

        @Override
        public int hashCode() {
            if (team1.name.compareTo(team2.name) >= 0)
                return Objects.hash(team1, team2);
            else
                return Objects.hash(team2, team1);
        }

        @Override
        public String toString() {
            return "Game{" +
                    "team1=" + team1 +
                    ", team2=" + team2 +
                    '}';
        }
    }

И теперь остается только создать все возможные комбинации игр и добавить их в Set, который самостоятельно отсечет дубликаты, основываясь на перекрытых методах:

public static void main(String[] args) {
        List<Team> teams = IntStream.range(1, 5)
                .boxed()
                .map(i -> new Team(i.toString()))
                .toList();

        Set<Game> games = teams.stream()
                .flatMap(team -> teams.stream().map(team1 -> new Game(team, team1)))
                .collect(Collectors.toUnmodifiableSet());

        games.forEach(
                game -> System.out.println(game.toString())
        );

    }
→ Ссылка
Автор решения: insolor

Максимально простой (тривиальный) способ:

Два вложенных цикла, первый (по индексу i) идет от первого до последнего элемента, вложенный (по индексу j) идет от следующего элемента (от i + 1) до последнего - тогда изначально не будет игр "сам с собой" и обратных повторений, и никакие "дубликаты" не нужно будет удалять.

public static void main(String[] args) {
    List<String> items = List.of("Команда 1", "Команда 2", "Команда 3", "Команда 4", "Команда 5");

    for (int i = 0; i < items.size(); i++) {
        for (int j = i + 1; j < items.size(); j++) {
            System.out.printf("%s играет с %s\n", items.get(i), items.get(j));
        }
    }
}

Вывод:

Команда 1 играет с Команда 2
Команда 1 играет с Команда 3
Команда 1 играет с Команда 4
Команда 1 играет с Команда 5
Команда 2 играет с Команда 3
Команда 2 играет с Команда 4
Команда 2 играет с Команда 5
Команда 3 играет с Команда 4
Команда 3 играет с Команда 5
Команда 4 играет с Команда 5
→ Ссылка