我有一个.csv文件,如下所示。它有两列URL
和timestamp
。假设URL是字母数字和UTC格式的日期/时间。
URL,timestamp
URL1,2018-12-09T14:19:00+00:00
URL1,2018-12-09T10:13:00+00:00
URL2,2018-12-09T07:25:00+00:00
URL3,2018-12-09T06:19:00+00:00
URL2,2018-12-08T22:03:00+00:00
URL3,2018-12-08T21:30:00+00:00
URL3,2018-12-08T09:30:00+00:00
URL2,2018-12-07T23:30:00+00:00
我想找到一个给定的日期作为输入,哪个URL被访问最多。
eg : Input : 2018-12-09
Output : URL1
Explaination: We can see above that URL1 has 2 entries on 2018-12-09 ,so it was visited the most times on that day.
我实现上述目标的想法是使用HashMap<String , List<String>>
来存储date as Key
和URL's as List of values for that Key
。
我在解析这个文件时遇到了问题。我怎么能
解析时间戳列并将Date存储为
Key
在Map中,因为有重复的日期?我假设一旦我们可以缩小上述范围,我们可以将URL作为值存储在该键的列表中。我们可以从映射到Array的映射中提取对应Key的值,然后检查大多数重复的URL。这是一个正确的方法吗?
我没有使用任何第三方库。
以上任何意见都是非常感谢的。
我建议您创建一个自定义类型,例如Visit
,如下所示,以结构化的方式处理记录。您可以使用标准的Java I/O库或一些专门的库从文件中读取记录并填充Visit
对象。我建议您为此使用流行的opensv库。无论您以何种方式填充Visit
对象并创建这些对象的List<Visit>
,都可以使用Java流API来处理列表。为了这个演示,我手动创建了一个List<Visit>
,其中包含您的问题中显示的记录。
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
class Visit {
private String url;
private OffsetDateTime dateTime;
public Visit(String url, OffsetDateTime dateTime) {
this.url = url;
this.dateTime = dateTime;
}
public String getUrl() {
return url;
}
public OffsetDateTime getDateTime() {
return dateTime;
}
}
public class Main {
public static void main(String[] args) {
List<Visit> list = List.of(
new Visit("URL1", OffsetDateTime.parse("2018-12-09T14:19:00+00:00")),
new Visit("URL1", OffsetDateTime.parse("2018-12-09T10:13:00+00:00")),
new Visit("URL2", OffsetDateTime.parse("2018-12-09T07:25:00+00:00")),
new Visit("URL3", OffsetDateTime.parse("2018-12-09T06:19:00+00:00")),
new Visit("URL2", OffsetDateTime.parse("2018-12-08T22:03:00+00:00")),
new Visit("URL3", OffsetDateTime.parse("2018-12-08T21:30:00+00:00")),
new Visit("URL3", OffsetDateTime.parse("2018-12-08T09:30:00+00:00")),
new Visit("URL2", OffsetDateTime.parse("2018-12-07T23:30:00+00:00"))
);
// Tests
System.out.println(mostVisitedUrlByDate(list, LocalDate.of(2018, 12, 9)));
System.out.println(mostVisitedUrlByDate(list, LocalDate.now(ZoneOffset.UTC)));
System.out.println(mostVisitedUrlByDate(null, LocalDate.of(2018, 12, 9)));
}
static String mostVisitedUrlByDate(List<Visit> list, LocalDate date) {
return list!=null ? list.stream() // Traverse list if it is not null
.filter(e -> e.getDateTime().toLocalDate().equals(date))
// Get a Map<String, List<Visit>> where value is the list of Visit objects grouped on the URL
.collect(Collectors.groupingBy(Visit::getUrl))
.values()
// Stream of values of Map obtained as a result of grouping
.stream()
// Find the value i.e. List<Visit> of largest size
.max(Comparator.comparing(List::size))
//A List<Visit> with a default Visit object
.orElse(List.of(new Visit("Unknown", OffsetDateTime.now(ZoneOffset.UTC))))
// All Visit elements in the value have same URL. Get the URL of the first value
.get(0)
.getUrl()
// Return "Unknown" if list is null
:"Unknown";
}
}
输出:URL1
Unknown
Unknown
我在代码中添加了足够的注释,使您更容易理解。此外,我对mostVisitedUrlByDate
方法进行了三个测试调用,以便您可以清楚地理解Stream
和Optional
上每个方法调用的目的。
我创建了一个CSV文件,其中包含您的问题中的示例数据,并将其命名为recorder.csv
。
使用Java流API和接口Multiset -来自谷歌番石榴库-由@fluffy在他的评论中建议。
(代码后的说明)
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class MostHits {
public static void main(String[] args) {
LocalDate search = LocalDate.parse("2018-12-09", DateTimeFormatter.ISO_LOCAL_DATE);
Path path = Paths.get("recorder.csv");
Supplier<Map<LocalDate, Multiset<String>>> supplier = () -> new HashMap<>();
BiConsumer<Map<LocalDate, Multiset<String>>, String> accumulator = (map, line) -> {
String[] flds = line.split(",");
LocalDate key = LocalDate.parse(flds[1], DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Multiset<String> multiset;
if (map.containsKey(key)) {
multiset = map.get(key);
}
else {
multiset = HashMultiset.create();
}
multiset.add(flds[0]);
map.put(key, multiset);
};
BiConsumer<Map<LocalDate, Multiset<String>>, Map<LocalDate, Multiset<String>>> combiner =
(map1, map2) -> {};
try {
Map<LocalDate, Multiset<String>> map = Files.lines(path)
.collect(supplier, accumulator, combiner);
Multiset<String> value = map.get(search);
if (value != null) {
Optional<String> most = value.elementSet()
.stream()
.max((s1, s2) -> value.count(s1) - value.count(s2));
most.ifPresentOrElse(System.out::println,
() -> System.out.println("No max found."));
}
else {
System.out.println("No value for: " + search);
}
}
catch (IOException xIo) {
xIo.printStackTrace();
}
}
}
- 我使用了一个任意的搜索键。您可以以任何您想要的方式获得搜索关键字,例如接受用户的值。
- 我从CSV文件中的行创建
Map
。Map
键是CSV文件中每行的日期字段,Map
值是一个Multiset
,它包含每个元素一次,但也包含每个元素被添加到Multiset
的次数。 - 读取整个文件后,我得到与搜索键相关的
Map
值。 - 如果在
Map
中有一个搜索键的条目,我从与搜索键相关联的Multiset
中获得具有最高计数的元素。 - 我打印出在搜索日期时获得最多点击的URL。