2016-11-23 41 views
2

我必須編寫程序,它應該讀取文件的字符並顯示word +他的字典。 txt文件是非常大的,使用掃描儀後,listOfWords尺寸爲:25000。用Java搜索anagrams 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-Stream編寫它,但我不知道。可以使用Streams從URL中讀取並搜索字符?你能幫我通過Stream搜索anagrams嗎?老師告訴我,代碼應該更短,我的閱讀整個列表。只有幾行,這是可能的嗎?

回答

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,只是StringReadernew InputStreamReader(new URL("www.xxx.pl/textFile.txt").openStream(), StandardCharsets.UTF_8)


替換如果要提取的第一個元素anagram集合,解決方案應該稍微修改:

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())))); 
} 

這裏的結果是映射,其中鍵是anagram集中的第一個元素(首先出現在輸入文件中),值是其餘元素按字母順序排序(我讓一個子列表跳過第一個元素,然後移動他們分成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(s.split( 「」))'?儘管你在同一個答案中使用了'Pattern.splitAsStream'?不要說*更高效的's.codePoints()。sorted().collect(StringBuilder :: new,StringBuilder :: appendCodePoint,StringBuilder :: append).toString();'。儘管使用了'char [] a = s.toCharArray(); Arrays.sort(一);返回String.valueOf(a);'這裏可能是更簡單的選擇。 – Holger

+0

好的工作,你能告訴我在哪裏可以添加我自己的排序實現,這將排序字謎(沒有第一個字)? – Khalos

+0

@Holger,沒有人問最有效的解決方案,只請求了基於Stream API的解決方案。如果你在這裏遇到性能問題,你不應該首先使用流(順便說一句,在這種情況下使用'CharBuffer.wrap(a)'作爲鍵可能更有效)。如果您只需要Stream API,那麼我的解決方案肯定比您的替代方案更短,更易於理解。 –