我正在开发一个 Java 应用程序,它读取了很多字符串数据,如下所示:
1 cat (first read)
2 dog
3 fish
4 dog
5 fish
6 dog
7 dog
8 cat
9 horse
...(last read)
我需要一种方法来保持所有情侣[字符串,事件] 从上次读取到第一次读取的顺序。
字符串出现
马1(第一次印刷)
猫2
狗4
鱼2(最后一次印刷)
实际上我使用两个列表:
1)List<string> input;
我添加所有数据
在我的例子中:
input.add("cat");
input.add("dog");
input.add("fish");
...
2)List<string> possibilities;
我以这种方式插入字符串一次:
if(possibilities.contains("cat")){
possibilities.remove("cat");
}
possibilities.add("cat");
通过这种方式,我有一个排序列表,其中包含所有可能性。 我这样用它:
int occurrence;
for(String possible:possibilities){
occurrence = Collections.frequency(input, possible);
System.out.println(possible + " " + occurrence);
}
这个技巧很好但是太慢了(我有数百万的输入)...任何帮助?
(英语不是我的第一语言,所以请原谅任何错误。)
答案 0 :(得分:1)
使用Map<String, Integer>
,@radoslaw指出,使插入排序使用LinkedHashMap
,而不是here所述的TreeMap
:
LinkedHashMap
保持按键的顺序,而TreeMap
则通过比较器或元素的自然可比较顺序进行排序。
想象一下,你有一些数组中的所有字符串,称之为listOfAllStrings
,迭代这个数组并在地图中使用字符串key
,如果它不存在,放在地图中,如果存在,则将1加到实际结果中......
Map<String, Integer> results = new LinkedHashMap<String, Integer>();
for (String s : listOfAllStrings) {
if (results.get(s) != null) {
results.put(s, results.get(s) + 1);
} else {
results.put(s, 1);
}
}
答案 1 :(得分:0)
使用TreeMap,它将按照MyStringComparator类的compare
指定的键来处理密钥,处理MyString类,它包装了String添加插入索引,如下所示:
// this better be immutable
class MyString {
private MyString() {}
public static MyString valueOf(String s, Long l) { ... }
private String string;
private Long index;
public hashcode(){ return string.hashcode(); }
public boolean equals() { // return rely on string.equals() }
}
class MyStringComparator implements Comparator<MyString> {
public int compare(MyString s1, MyString s2) {
return -s1.getIndex().compareTo(s2.gtIndex());
}
}
在构建地图时传递比较器:
Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator());
然后,在解析输入时,执行
Long counter = 0;
while (...) {
MyString item = MyString.valueOf(readString, counter++);
if (map.contains(item)) {
map.put(map.get(item)+1);
} else {
map.put(item,1);
}
}
由于不可变类会有很多实例化,并且比较器与equals不一致,但它应该有效。
免责声明:这是未经测试的代码,只是为了展示我所做的事情,当我开始使用编译器时,我会回来并重新检查它。
答案 2 :(得分:0)
以下是您问题的完整解决方案,
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DataDto implements Comparable<DataDto>{
public int count = 0;
public String string;
public long lastSeenTime;
public DataDto(String string) {
this.string = string;
this.lastSeenTime = System.currentTimeMillis();
}
public boolean equals(Object object) {
if(object != null && object instanceof DataDto) {
DataDto temp = (DataDto) object;
if(temp.string != null && temp.string.equals(this.string)) {
return true;
}
}
return false;
}
public int hashcode() {
return string.hashCode();
}
public int compareTo(DataDto o) {
if(o != null) {
return o.lastSeenTime < this.lastSeenTime ? -1 : 1;
}
return 0;
}
public String toString() {
return this.string + " : " + this.count;
}
public static final void main(String[] args) {
String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"};
Map<String, DataDto> results = new HashMap<String, DataDto>();
for (String s : listOfAllStrings) {
DataDto dataDto = results.get(s);
if(dataDto != null) {
dataDto.count = dataDto.count + 1;
dataDto.lastSeenTime = System.nanoTime();
} else {
dataDto = new DataDto(s);
results.put(s, dataDto);
}
}
List<DataDto> finalResults = new ArrayList<DataDto>(results.values());
System.out.println(finalResults);
Collections.sort(finalResults);
System.out.println(finalResults);
}
}
答
[horse : 1, cat : 2, fish : 2, dog : 1]
[fish : 2, horse : 1, cat : 2, dog : 1]
我认为此解决方案适合您的要求。
答案 3 :(得分:0)
如果您在将数据全部读入内存时知道数据不会超出内存容量,那么解决方案很简单 - 使用LinkedList
或a和LinkedHashMap
。
例如,如果您使用链接列表:
LinkedList<String> input = new LinkedList();
然后按照原来的方式继续使用input.add()
。但是当输入列表已满时,您基本上使用Jordi Castilla的解决方案 - 但是将条目放在链接列表中的逆序中。要做到这一点,你可以:
Iterator<String> iter = list.descendingIterator();
LinkedHashMap<String,Integer> map = new LinkedHashMap<>();
while (iter.hasNext()) {
String s = iter.next();
if ( map.containsKey(s)) {
map.put( s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
现在,他的解决方案与我的解决方案之间唯一真正的区别在于我使用list.descendingIterator()
这是LinkedList
中的一种方法,它以向后的顺序为您提供条目,从“马”到“马”猫”。
LinkedHashMap
将保持正确的顺序 - 首先输入的内容将首先打印,因为我们以相反的顺序输入内容,所以最后读取的内容将首先打印。因此,如果您打印map
,结果将是:
{horse=1, cat=2, dog=4, fish=2}
如果你有一个很长的文件,并且你无法将整个字符串列表加载到内存中,那么最好只保留频率图。在这种情况下,为了保持输入顺序,我们将使用如下对象:
private static class Entry implements Comparable<Entry> {
private static long nextOrder = Long.MIN_VALUE;
private String str;
private int frequency = 1;
private long order = nextOrder++;
public Entry(String str) {
this.str = str;
}
public String getString() {
return str;
}
public int getFrequency() {
return frequency;
}
public void updateEntry() {
frequency++;
order = nextOrder++;
}
@Override
public int compareTo(Entry e) {
if ( order > e.order )
return -1;
if ( order < e.order )
return 1;
return 0;
}
@Override
public String toString() {
return String.format( "%s: %d", str, frequency );
}
}
这里的技巧是每次更新条目(在频率上加1)时,它也会更新订单。但是compareTo()
方法从高顺序(稍后更新/插入)到低顺序(先前更新/插入)命令Entry
个对象。
现在,您可以使用简单的HashMap<String,Entry>
在阅读时存储信息(我假设您正在阅读某种扫描仪):
Map<String,Entry> m = new HashMap<>();
while ( scanner.hasNextLine() ) {
String str = scanner.nextLine();
Entry entry = m.get(str);
if ( entry == null ) {
entry = new Entry(str);
m.put(str, entry);
} else {
entry.updateEntry();
}
}
Scanner.close();
现在您可以对条目的值进行排序:
List<Entry> orderedList = new ArrayList<Entry>(m.values());
m = null;
Collections.sort(orderedList);
正在运行System.out.println(orderedList)
会给您:
[horse: 1, cat: 2, dog: 4, fish: 2]
原则上,你可以使用一个TreeMap
,其中的键包含“order”内容,而不是像这样的普通HashMap
,然后进行排序,但我不想在地图中使用任何可变键,也不断改变密钥。这里我们只是在填充地图时更改值,每个键只插入一次地图。
答案 4 :(得分:0)
你能做什么:
代码:
/* I don't know what logic you use to create the input list,
* so I'm using your input example. */
List<String> input = Arrays.asList("cat", "dog", "fish", "dog",
"fish", "dog", "dog", "cat", "horse");
/* by the way, this changes the input list!
* Copy it in case you need to preserve the original input. */
Collections.reverse(input);
Set<String> possibilities = new LinkedHashSet<String>(strings);
for (String s : possibilities) {
System.out.println(s + " " + Collections.frequency(strings, s));
}
输出:
horse 1
cat 2
dog 4
fish 2