2016-11-23 3 views
2

Мне нужно написать программу, которая должна читать файл для анаграмм и показывать слово + его анаграммы. Txt файлы очень большие, после использования сканера, размер listOfWords является: 25000.Поиск анаграммы с Java 8

Выходной пример:

word anagram1 anagram2 anagram3 ... 
word2 anagram1 anagram2... 

У меня есть код, он работает, но очень медленно:

private static List<String> listOfWords = new ArrayList<String>(); 
    private static List<ArrayList<String>> allAnagrams = new ArrayList<ArrayList<String>>(); 

    public static void main(String[] args) throws Exception { 
    URL url = new URL("www.xxx.pl/textFile.txt"); 
    Scanner scanner = new Scanner(url.openStream()); 
    while (scanner.hasNext()) { 
     String nextToken = scanner.next(); 
     listOfWords.add(nextToken); 
    } 
    scanner.close(); 

    while (listOfWords.isEmpty() == false) { 
     ArrayList<String> anagramy = new ArrayList<String>(); 
     String wzor = listOfWords.remove(0); 
     anagramy.add(wzor); 
     char[] ch = wzor.toCharArray(); 
     Arrays.sort(ch); 
     for (int i = 0; i < listOfWords.size(); i++) { 
     String slowo = listOfWords.get(i); 
     char[] cha = slowo.toCharArray(); 
     Arrays.sort(cha); 
     if (Arrays.equals(ch, cha)) { 
      anagramy.add(slowo); 
      listOfWords.remove(i); 
      i--; 
     } 
     } 
     allAnagrams.add(anagramy); 
    } 

    for (ArrayList<String> ar : allAnagrams) { 
     String result = ""; 
     if (ar.size() > 1) { 
     for (int i = 1; i < ar.size(); i++) { 
      result = ar.get(i) + " "; 
     } 
     System.out.println(ar.get(0) + " " + result); 
     } 
    } 
    } 

Я должен пишите его с Java 8 - потоками, но я не знаю. Можно использовать Streams для чтения из URL + поисковых анаграмм? Не могли бы вы помочь мне в поиске анаграмм Stream? Учитель сказал мне, что код должен быть короче, чтобы мой читал весь список. Это возможно только в нескольких строках?

ответ

3

Вы можете прочитать слова из файла в список или непосредственно создавать поток его:

try (InputStream is = new URL("http://www.someurl.pl/file.txt").openConnection().getInputStream(); 
    BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
    Stream<String> stream = reader.lines()) { 
     //do something with stream 
} 

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

Map<String, List<String>> anagrams = 
    stream.collect(Collectors.groupingBy(w -> sorted(w))); 

Отсортированное метод просто сортировки писем, как вы делали в вашем примере:

public static String sorted(String word) { 
    char[] chars = word.toCharArray(); 
    Arrays.sort(chars); 
    return new String(chars); 
} 
2

Давайте создадим отдельный метод, который сортирует буквы. Вы можете сделать это с помощью потока API, а также:

private static String canonicalize(String s) { 
    return Stream.of(s.split("")).sorted().collect(Collectors.joining()); 
} 

Теперь вы можете прочитать некоторые Reader, извлекать из него слова и группы слов канонической формы:

Map<String, Set<String>> map = new BufferedReader(reader).lines() 
      .flatMap(Pattern.compile("\\W+")::splitAsStream) 
      .collect(Collectors.groupingBy(Anagrams::canonicalize, Collectors.toSet())); 

Далее, вы можете удалить отдельные буквенные группы используя Stream API в третий раз:

return map.values().stream().filter(list -> list.size() > 1).collect(Collectors.toList()); 

Теперь вы можете передать читателю этот код, чтобы извлечь из него анаграммы. Вот полный код:

import java.io.*; 
import java.util.*; 
import java.util.regex.Pattern; 
import java.util.stream.*; 

public class Anagrams { 
    private static String canonicalize(String s) { 
     return Stream.of(s.split("")).sorted().collect(Collectors.joining()); 
    } 

    public static List<Set<String>> getAnagrams(Reader reader) { 
    Map<String, Set<String>> map = new BufferedReader(reader).lines() 
            .flatMap(Pattern.compile("\\W+")::splitAsStream) 
            .collect(Collectors.groupingBy(Anagrams::canonicalize, Collectors.toSet())); 
     return map.values().stream().filter(list -> list.size() > 1).collect(Collectors.toList()); 
    } 

    public static void main(String[] args) throws IOException { 
     getAnagrams(new StringReader("abc cab tat aaa\natt tat bbb")) 
       .forEach(System.out::println); 
    } 
} 

Он печатает

[att, tat] 
[abc, cab] 

Если вы хотите использовать URL, просто замените StringReader с new InputStreamReader(new URL("www.xxx.pl/textFile.txt").openStream(), StandardCharsets.UTF_8)


Если вы хотите извлечь первый элемент набор анаграмм, раствор следует слегка изменить:

public static Map<String, Set<String>> getAnagrams(Reader reader) { 
    Map<String, List<String>> map = new BufferedReader(reader).lines() 
     .flatMap(Pattern.compile("\\W+")::splitAsStream) 
     .distinct() // remove repeating words 
     .collect(Collectors.groupingBy(Anagrams::canonicalize)); 
    return map.values().stream() 
     .filter(list -> list.size() > 1) 
     .collect(Collectors.toMap(list -> list.get(0), 
           list -> new TreeSet<>(list.subList(1, list.size())))); 
} 

Здесь отображается карта, в которой ключ является первым элементом в наборе анаграмм (сначала это произошло во входном файле), а значение - это остальные элементы, отсортированные в алфавитном порядке (я делаю подсписку, чтобы пропустить первый элемент, а затем переместить их в TreeSet выполнить сортировку; альтернативой будет list.stream().skip(1).sorted().collect(Collectors.toList())).

Пример использования:

getAnagrams(new StringReader("abc cab tat aaa\natt tat bbb\ntta\ncabr\nrbac cab crab cabrc cabr")) 
     .entrySet().forEach(System.out::println); 
+2

Действительно? 'Stream.of (ДЕЛЕНИЕ (""))'? Несмотря на то, что вы используете 'Pattern.splitAsStream' в том же ответе? Не говоря уже о * намного более эффективном 's.codePoints(). Sorted() .collect (StringBuilder :: new, StringBuilder :: appendCodePoint, StringBuilder :: append) .toString();'. Хотя использование 'char [] a = s.toCharArray(); Arrays.sort (а); return String.valueOf (a); 'может быть более простым выбором здесь. – Holger

+0

Хорошая работа, не могли бы вы сказать мне, где я могу добавить свою собственную реализацию сортировки, которая будет сортировать анаграммы (без первого слова)? – Khalos

+0

@ Holger, никто не задавал наиболее эффективное решение, было запрошено только решение на основе Stream API. Если у вас есть проблемы с производительностью, вы не должны использовать потоки в первую очередь (кстати, в этом случае использование 'CharBuffer.wrap (a)' как ключа, вероятно, более эффективно). Если вы просто хотите Stream API, то мое решение определенно короче и понятнее, чем ваши альтернативы. –