Java 计算字母数字字符的出现并以图形方式打印它们
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18766857/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Counting the occurrence of alphanumeric characters and printing them graphically
提问by swennemen
I have a string, and I want to count the occurrence of all letters and numbers and want to create a graph so I can see the occurrence graphically.
我有一个字符串,我想计算所有字母和数字的出现次数,并想创建一个图表,以便我可以以图形方式查看出现次数。
So for example:
例如:
String sentence = "ABC ABC ABC 123"
A (3) * * *
B (3) * * *
C (3) * * *
D
.
.
My way of thinking:
我的思路:
- Count all numbers and letters in the string
- Print all asterisks times this number (unfortunately I can't multiply a String with an int in Java)
- 计算字符串中的所有数字和字母
- 打印所有星号乘以这个数字(不幸的是,我不能在 Java 中将 String 与 int 相乘)
I think there are two ways of counting the characters. I can either use the charAt()
method or toCharArray()
and loop through the string or array and count the letters.
我认为有两种计算字符的方法。我可以使用该charAt()
方法或toCharArray()
循环遍历字符串或数组并计算字母。
For example:
例如:
aCounter = 0;
bCounter = 0;
char ch = sentence.charAt(i);
for (i = 0; i < sentence.length(); ++i) {
if (ch == 'a') {
aCounter++;
}
if (ch == 'b') {
bCounter++;
}
}
However, I have multiple problems with this approach:
但是,我对这种方法有多个问题:
- I would have a to make a lot of counter variables -
aCounter
throughzCounter
plus0counter
through9counter
- I would have to make another for loop to print the asterisks!
- 我需要制作很多计数器变量 -
aCounter
通过zCounter
加上0counter
通过9counter
- 我必须再做一个 for 循环来打印星号!
I'm not asking for an set answer here, I'm just looking for some good directions, because I'm stuck.
我不是在这里要求固定答案,我只是在寻找一些好的方向,因为我被卡住了。
回答by crush
There's no need to make a HashTable/HashMap/HashSet
for this.
没有必要为此做一个HashTable/HashMap/HashSet
。
You know which characters are being tracked ahead of time, so you can use an array.
您提前知道正在跟踪哪些字符,因此您可以使用数组。
I want to count the occurrence of all letters and numbers
我想计算所有字母和数字的出现次数
Make a string of the characters you will track, then initialize an array.
制作一串您要跟踪的字符,然后初始化一个数组。
String sentence = "ABC ABC ABC 123";
//Make a map of all the characters you want to track.
String indexes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//Initialize an array to the size of the possible matches.
int[] count = new int[indexes.length()];
//Loop through the sentence looking for matches.
for (int i = 0; i < sentence.length(); i++) {
//This will get the index in the array, if it's a character we are tracking
int index = indexes.indexOf(sentence.charAt(i));
//If it's not a character we are tracking, indexOf returns -1, so skip those.
if (index < 0)
continue;
count[index]++;
}
Then you can print them all out with this:
然后你可以用这个把它们全部打印出来:
for (int i = 0; i < count.length; i++) {
if (count[i] < 1)
continue;
System.out.println(String.format("%s (%d) %s",
indexes.charAt(i),
count[i],
//This little bit of magic creates a string of nul bytes, then replaces it with asterisks.
new String(new char[count[i]]).replace('1 (1) *
2 (1) *
3 (1) *
A (3) ***
B (3) ***
C (3) ***
', '*')));
}
If you aren't comfortable with the new String(new char[count[i]]).replace('\0', '*'))
bit, then you can use a StringBuilder
to build the asterisk String
before trying to output it. You can see @mike's example below for a good example of that.
如果您对这个new String(new char[count[i]]).replace('\0', '*'))
位不满意,那么您可以在尝试输出它之前使用 aStringBuilder
来构建星号String
。你可以在下面看到@mike的例子,这是一个很好的例子。
Outputs
输出
String str = "abc abc abc 123";
Hashtable numbers = new Hashtable();
int size = str.length();
for(int i = 0 ; i< size ; i++)
{
char curr = str.charAt(i);
if(numbers.contains(curr) == false)
{
numbers.put(curr, 1);
}
else
{
numbers.put(curr, ((int)numbers.get(curr)) + 1);
}
}
Enumeration names = numbers.keys();
char c;
while(names.hasMoreElements()) {
c = (char) names.nextElement();
System.out.println(c + ": " +
numbers.get(c));
}
Considerations
注意事项
Here are some things to consider when decided how to solve this problem.
以下是决定如何解决此问题时需要考虑的一些事项。
- Will you always know what characters need to be tracked ahead of time, or will there be times when you want to track anycharacter? In the latter case, an array won't work for you; you would need to use an advanced data structure like a TreeMap or HashMap.
- Will you always be counting occurrences of specific
char
s, as opposed toString
s? If you have to modify this to countString
s then using theString indexes
map trick isn't going to work for you either. - Are you learning a specific data structure(s) in your course at the moment? Usually problems such as this are assigned to students to understand how to apply a specific concept. As @kylesuggested, you should try to use the data structure that you are learning about, or have learned about, in your class. Sometimes using structures that you haven't learned about yet can get you in trouble, or a lower grade at least.
- 您是否总是知道需要提前跟踪哪些角色,或者有时您想跟踪任何角色?在后一种情况下,数组对您不起作用;您需要使用高级数据结构,如 TreeMap 或 HashMap。
- 你会一直计算特定
char
s 的出现次数,而不是String
s 吗?如果您必须将其修改为 countString
s,那么使用String indexes
map 技巧也不适合您。 - 您目前是否正在课程中学习特定的数据结构?通常将此类问题分配给学生,以了解如何应用特定概念。正如@kyle建议的那样,您应该尝试使用您在课堂上正在学习或已经了解的数据结构。有时使用你还没有学过的结构会给你带来麻烦,或者至少会降低成绩。
回答by No Idea For Name
create a hashtable and go through the string, add each time the current char to the hashtable
创建一个哈希表并遍历字符串,每次将当前字符添加到哈希表中
Map<Character,String> results = new HashMap<Character, String>();
回答by Joni
Use an array to store the counters. You can use a char directly as an array index, so you don't need complex logic.
使用数组来存储计数器。您可以直接使用 char 作为数组索引,因此不需要复杂的逻辑。
To print a given number of asterisks, a for loop is the easiest way.
要打印给定数量的星号,for 循环是最简单的方法。
回答by Stephen C
Here are some hints to get you started:
以下是一些帮助您入门的提示:
Don't use a separate variable for each counter. Use an array (or some collection type ... if you have been taught about that ...).
You can use a character as an array index.
Accumulate all of the counts before you start printing anything.
不要为每个计数器使用单独的变量。使用数组(或某种集合类型......如果你已经学会了......)。
您可以使用字符作为数组索引。
在开始打印任何内容之前累积所有计数。
回答by Fritz
Instead of looping once to calculate the amount, and looping a second time to print the asterisks, you can use another approach:
您可以使用另一种方法,而不是循环一次来计算数量,然后循环第二次来打印星号:
If the map contains data for the key
Obtain the data for the character
append a new asterisk
Else
Create a String with an asterisk
Append an asterisk
Put the String with the character as key
Then, each time you iterate, you check if your map contains data for the character and, if it doesn't, you initialize it. In pseudocode:
然后,每次迭代时,检查地图是否包含角色的数据,如果没有,则对其进行初始化。在伪代码中:
public static String alphNum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
public static String count(char c, String str) {
String stringToReturn = Character.toString(c);
for(char ch : str.toCharArray()) {
if (ch == c) {
stringToReturn += " *";
}
}
return stringToReturn;
}
public static void countAndPrintAlphNum(String str) {
String stringToTest = str.toUpperCase();
Set<String> rows = new HashSet<String>();
char[] alphNumArray = alphNum.toCharArray();
for(char c : alphNumArray) {
rows.add(count(c, stringToTest));
}
for(String row : rows) {
System.out.println(row);
}
}
public static void main(String[] args) {
countAndPrintAlphNum("Hi There 123!");
}
If you ever need the amount of asterisks as a number, you can always obtain the size of that String
(assuming you don't put any whitespaces).
如果您需要将星号的数量作为数字,您始终可以获得它的大小String
(假设您没有放置任何空格)。
Update
更新
As an enhancement, taking into account the comments I shared with @crush, two tweaks can improve the logic:
作为增强,考虑到我与@crush 分享的评论,有两个调整可以改进逻辑:
StringBuilder
instead ofString
:Avoid unnecessary creation of literals.TreeMap
instead ofHashMap
:It would give the proper order to the map, allowing a sorted printing of its content.
StringBuilder
而不是String
:避免不必要的文字创建。TreeMap
而不是HashMap
:它将为地图提供正确的顺序,允许对其内容进行排序打印。
It's up to the OP to add this extra stuff, if there's room (and knowledge) enough to justify their use.
如果有足够的空间(和知识)来证明它们的使用是合理的,则由 OP 添加这些额外的东西。
回答by James Dunn
Divide it into two methods-- one to create a String "row" given a char and a String, and one to call the first method for each of the 36 alphanumeric chars.
将它分为两种方法——一种是创建一个字符串“行”给定一个字符和一个字符串,另一种是为 36 个字母数字字符中的每一个调用第一个方法。
public class StringHistogram
{
public static void main(String[] args) throws IOException
{
Scanner sc = new Scanner(System.in);
System.out.print("Please insert string: ");
String s = sc.nextLine();
sc.close();
System.out.println(s);
StringReader r = new StringReader(s);
Map<Character, Integer> histogram = new TreeMap<Character, Integer>();
int c;
while ((c = r.read()) != -1) {
Integer count = histogram.get((char) c);
if (count == null)
count = 0;
histogram.put((char) c, count + 1);
}
r.close();
for (Entry<Character, Integer> entry : histogram.entrySet())
System.out.println(entry.getKey() + " (" + entry.getValue()
+ ") " + createAsterisk(entry.getValue()));
}
private static String createAsterisk(int number) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < number; i++)
sb.append("*");
return sb.toString();
}
}
Note: If you want to ensure that the rows are printed in alpha - numeric order (with the numbers first), use a TreeSet instead of a HashSet for the rows.
注意:如果要确保以字母-数字顺序打印行(数字在前),请对行使用 TreeSet 而不是 HashSet。
回答by mike
Here an OOP approach, that uses StringReader
and a Map. I used TreeMap
to have the ouput sorted.
这是一个 OOP 方法,它使用StringReader
一个 Map。我TreeMap
曾经对输出进行排序。
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;
}
}
回答by Kyle
Since you are new to this and don't already have the solution (Which is where everyone starts), the rightanswer is to use the data structure that you are learning in class.
由于您对此不熟悉并且还没有解决方案(这是每个人都开始的地方),因此正确的答案是使用您在课堂上学习的数据结构。
If you are learning Maps
如果你正在学习地图
- TreeMap sorts but natural order of the key (Nice for printing)
- HashMap doesn't have a very predictable ordering
- TreeMap 排序但键的自然顺序(适合打印)
- HashMap 没有非常可预测的排序
If you are learning arrays, there are great examples in this thread already ex. response from crush
如果您正在学习数组,那么此线程中已经有很好的示例。来自暗恋的回应
回答by mike
Since, we're living in times of cloud computing and parallelism. Here another approach.
因为,我们生活在云计算和并行化时代。这是另一种方法。
public class MinimalisticHistogram
{
public static void main(String[] args)
{
int[] occurrences = new int[(int) Math.pow(2, 16)]; // 256 KB
for (char c : new Scanner(System.in).nextLine().toCharArray()) occurrences[c]++;
for (int i = 0; i < occurrences.length; i++) if (occurrences[i] != 0) System.out.println(String.format("%c %4s %s", i, "(" + occurrences[i] + ")", new String(new char[occurrences[i]]).replace('##代码##', '*')));
}
}
回答by mike
Here, a last, minimalistic notOOP answer. With effectively 3 lines. It works, because chars can be interpreted as integers.
I was a little concerned about not closing the scanner. But since the javadoc says System.in
is already open and ready to supply input data
. I assume that the closing of the resource is also handled by the system.
在这里,最后一个简约的非OOP 答案。有效地 3 行。它有效,因为字符可以解释为整数。
我有点担心不关闭扫描仪。但是由于 javadoc 说System.in
is already open and ready to supply input data
. 我假设资源的关闭也是由系统处理的。