统计字母数字字符的出现次数并以图形化方式打印显示

7

我有一个字符串,想要计算所有字母和数字的出现次数,并创建一个图表以便可以直观地查看出现情况。

例如:

String sentence = "ABC ABC ABC 123"

A (3) * * *
B (3) * * *
C (3) * * *
D
.
.

我的思路:

  1. 计算字符串中所有数字和字母的数量
  2. 打印所有星号,次数为该数字(不幸的是,在Java中我无法将一个String与int相乘)

我认为有两种计算字符数量的方法。我可以使用charAt()方法或toCharArray()并循环遍历字符串或数组并计算字母。

例如:

aCounter = 0;
bCounter = 0;
char ch = sentence.charAt(i);

for (i = 0; i < sentence.length(); ++i) {
    if (ch == 'a') {
        aCounter++;
    }
    if (ch == 'b') {
        bCounter++;
    }
}

然而,我对这种方法有多个问题:

  • 我需要创建大量的计数器变量 - 从 aCounterzCounter 再到 0counter9counter
  • 我还需要再创建一个循环来打印星号!

我这里不是要求得出正确答案,只是想寻求一些好的方向,因为我现在陷入困境了。


5
使用哈希表,以字符作为键。然后,在伪代码中简单地执行 map[character]++ 即可。 - Marc B
哦,你想要每个字符出现的次数。咦,我知道这看起来太简单了。不是说它不容易。Marc B 有正确的想法。 - crush
你可以使用 Map<Char, Integer> 或者数组,因为你知道字母和数字的总数。 - crush
大小写是否重要?还是应该将大写字母A和小写字母a视为相同的字符? - crush
变量charCount = 0 变量c = null; for (var i = 0; i < newPwd.value.length; i++) { c = newPwd.value.substring(i, i + 1) 如果(isNaN(parseInt(c)) == true) { charCount++ } } - hima
我建议使用int[]数组而不是产生HashMap的开销。看看我的示例与HashMap示例的性能表现会很有趣。 - crush
12个回答

0

所以我使用了一个双重循环来计算字符。如果一个字符在其中一个数组中匹配,计数将添加到第三个数组中。

for (int i = 0; i < zinArray.length; i++) {
    char c = zinArray[i];
    for (int j = 0; j < controleArray.length; j++) { 
        char d = controleArray[j];      
        if (c == d) {
        letterCount[j]++;
        break; 
        }
    }
}

0

由于我们生活在云计算和并行时代,这里提供另一种方法。

public class DistributedHistogram
{
  private static final int PORT = 1337;
  private static final String LOOPBACK = "127.0.13.37";

  public static final byte[] DATA = new byte[] {(byte) 0xFF, (byte) 0xFF};
  public static final byte[] STOP = new byte[] {(byte) 0xDE, (byte) 0xAD};

  public static void main(String[] args) throws IOException, InterruptedException
  {
    ExecutorService se = Executors.newSingleThreadExecutor();
    se.submit(new Server(PORT, 16));

    System.out.print("Please insert string: ");
    Scanner s = new Scanner(System.in);
    String input = s.nextLine();
    s.close();
    System.out.println(input);

    ExecutorService ce = Executors.newFixedThreadPool(16);
    List<Future<Void>> futures = new ArrayList<Future<Void>>();
    for (char c : input.toCharArray())
      futures.add(ce.submit(new Client(new Character[]{c}, DATA, LOOPBACK, PORT)));

    /* wait for the clients to complete before we send stop to server */
    for (Future<Void> f : futures)
    {
      try
      {
        @SuppressWarnings ("unused")
        Void v = f.get();
      }
      catch (ExecutionException e)
      {
        //...
      }
    }
    ce.submit(new StopClient(LOOPBACK, PORT)); // sends stop signal

    ce.shutdown();
    se.shutdown();
  }
}

class Client implements Callable<Void>
{
  private final Character[] chars;
  private final String ip;
  private final int port;
  private final byte[] type;

  public Client(Character[] chars, byte[] type, String ip, int port)
  {
    this.chars = chars;
    this.type = type;
    this.ip = ip;
    this.port = port;
  }

  @Override
  public Void call() throws Exception
  {
    Socket s = new Socket(ip, port);
    DataOutputStream out = new DataOutputStream(s.getOutputStream());
    for (Character c : chars) {
      out.write(type);
      out.writeChar(c);
    }
    out.flush();
    out.close();
    s.close();
    return null;
  }
}

class StopClient extends Client {

  public StopClient(String ip, int port)
  {
    super(new Character[]{' '}, DistributedHistogram.STOP, ip, port);
  }
}

class Server implements Callable<Void>
{
  private final int port;
  private ServerSocket ss;
  private final ExecutorService e;

  private final ConcurrentHistogram ch = new ConcurrentHistogram();
  private final AtomicInteger client = new AtomicInteger();

  private AtomicBoolean quit = new AtomicBoolean(false);

  public Server(int port, int clients)
  {
    this.port = port;
    this.e = Executors.newFixedThreadPool(clients);
  }

  public ConcurrentHistogram getHistogram() { return ch; }

  public void stop()
  {
    quit.set(true);
    e.submit(new Callable<Void>()
    {
      @Override
      public Void call() throws Exception
      {
        Thread.sleep(250);
        ss.close();
        return null;
      }
    });
  }

  @Override
  public Void call() throws Exception
  {
    ss = new ServerSocket(port);
    while (!quit.get() && !ss.isClosed())
    {
      try
      {
        e.submit(new ClientHandler(client.getAndIncrement(), ss.accept(), this));
      }
      catch (SocketException se)
      { continue; }
    }
    e.shutdown();
    System.out.println(ch.toString());
    while (!e.isTerminated()) { /* wait */ }
    return null;
  }
}

class ConcurrentHistogram
{
  private final ConcurrentMap<Character, AtomicInteger> histogram = new ConcurrentHashMap<Character, AtomicInteger>();
  private static final String HISTOGRAM_CHAR = "*";

  public ConcurrentMap<Character, AtomicInteger> getHistogram() { return histogram; }

  private String createAsterisk(int number)
  {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < number; i++)
      sb.append(HISTOGRAM_CHAR);
    return sb.toString();
  }

  @Override
  public String toString()
  {
    StringBuilder sb = new StringBuilder();
    List<Entry<Character, AtomicInteger>> data = new ArrayList<Entry<Character, AtomicInteger>>(histogram.entrySet());
    Collections.sort(data, new Comparator<Entry<Character, AtomicInteger>>()
    {
      @Override
      public int compare(Entry<Character, AtomicInteger> o1, Entry<Character, AtomicInteger> o2)
      {
        return o1.getKey().compareTo(o2.getKey());
      }
    });
    for (Entry<Character, AtomicInteger> entry : data)
    {
      int value = entry.getValue().get();
      sb.append(entry.getKey() + " " + String.format("%4s", "(" + value + ")") + " " + createAsterisk(value) + "\n");
    }
    return sb.toString();
  }

  public void addChar(Character c)
  {
    AtomicInteger value = histogram.get(c);
    if (value == null)
    {
      histogram.putIfAbsent(c, new AtomicInteger());
      value = histogram.get(c);
    }
    value.incrementAndGet();
  }
}

class ClientHandler implements Callable<Void>
{
  @SuppressWarnings ("unused")
  private final int client;
  private final Socket s;

  private final Server server;

  public ClientHandler(int client, Socket s, Server server)
  {
    this.client = client;
    this.s = s;
    this.server = server;
  }

  @Override
  public Void call() throws Exception
  {
    DataInputStream in = new DataInputStream(s.getInputStream());
    int c;
    int i = 0;
    byte[] bytes = new byte[2];
    while ((c = in.read()) != -1)
    {
      if (i < 2)
      { bytes[i++] = ((byte) c); }
      else if (Arrays.equals(bytes, DistributedHistogram.DATA))
      {
        i = 0;
        char ch = (char) (((c & 0x00FF) << 8) + (in.read() & 0x00FF));
        server.getHistogram().addChar(ch);
      }
      else if (Arrays.equals(bytes, DistributedHistogram.STOP))
      {
        i = 0;
        server.stop();
      }
      else
      { i = 0; }
    }
    in.close();
    s.close();
    return null;
  }
}

1
哈哈。如果要统计《圣经》中字符出现的次数,这将是一个不错的方法。 - crush
:D 花了我一段时间才让服务器在发送最后一条消息“之后”停止。单独使用 f.get() 等待并没有帮助,我不得不在关闭服务器套接字之前添加一个延迟... 另外,我添加了第三个也是最后一个解决方案。它尽可能简短,至少我认为是这样的。 - mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接