使用Java解析.csv文件时面临的问题



我有一个.csv文件,如下所示。它有两列URLtimestamp。假设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 KeyURL's as List of values for that Key

我在解析这个文件时遇到了问题。我怎么能

  1. 解析时间戳列并将Date存储为Key在Map中,因为有重复的日期?

  2. 我假设一旦我们可以缩小上述范围,我们可以将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方法进行了三个测试调用,以便您可以清楚地理解StreamOptional上每个方法调用的目的。

我创建了一个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();
        }
    }
}
  1. 我使用了一个任意的搜索键。您可以以任何您想要的方式获得搜索关键字,例如接受用户的值。
  2. 我从CSV文件中的行创建MapMap键是CSV文件中每行的日期字段,Map值是一个Multiset,它包含每个元素一次,但也包含每个元素被添加到Multiset的次数。
  3. 读取整个文件后,我得到与搜索键相关的Map值。
  4. 如果在Map中有一个搜索键的条目,我从与搜索键相关联的Multiset中获得具有最高计数的元素。
  5. 我打印出在搜索日期时获得最多点击的URL。

最新更新