使用 Java 8 搜索字谜词

问题描述 投票:0回答:4

我必须编写程序,该程序应该读取字谜文件并显示单词+他的字谜。 Txt文件很大,使用扫描仪后,listOfWords大小为:25000。

输出示例:

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 搜索字谜吗?老师告诉我,代码应该比我的阅读整个列表更短。只有几行,可以吗?

java java-8 java-stream anagram
4个回答
5
投票

您可以将文件中的单词读入列表或直接创建它的流:

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

4
投票

让我们创建一个单独的方法来对字母进行排序。您也可以使用 Stream 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);

0
投票

你可以试试这个方法

//---------------Anagram---------------------------------
    String w1 = "Triangle".toLowerCase(), w2 = "Integral".toLowerCase();
    HashMap<String, Integer> w1Map = new HashMap<String, Integer>();
    HashMap<String, Integer> w2Map = new HashMap<String, Integer>();

    w1Map = convertToHashMap(w1);
    w2Map = convertToHashMap(w2);       

   if( !(w1.equals(w2)) && (w1Map.keySet().equals(w2Map.keySet()))) 
       System.out.println(w1+" and "+w2+" are anagrams");
   else 
       System.out.println(w1+" and "+w2+" are not anagrams");

调用下面的方法

public static HashMap<String, Integer> convertToHashMap(String s) {
    HashMap<String, Integer> wordMap = new HashMap<String, Integer>();
    for (int i = 0;i < s.length(); i++){
        wordMap.put(String.valueOf(s.charAt(i)), Integer.valueOf(s.charAt(i)));
    }
    return wordMap;

0
投票

公共类 TestAnagram {

@Test
public void testAnagram1() {
    String[] anagram = {"listen", "silent"};
    int v = anagram[0].toLowerCase().chars().reduce((i, j) -> i + j).getAsInt() - anagram[1].toLowerCase().chars().reduce((i, j) -> i + j).getAsInt();
    Assert.assertEquals(v, 0);

}

}

© www.soinside.com 2019 - 2024. All rights reserved.