【问题标题】:Comparing two arrayLists from CSV比较 CSV 中的两个数组列表
【发布时间】:2019-04-28 07:28:56
【问题描述】:

我有一个一般性问题:

如果:

(1) 每个arrayList 只包含一列导入的CSV(通过inputStream 和bufferReader(为方便起见,下面不再打印)。

//my arrayLists:

List <String> OpenVal = new Arraylist ();
List <String> CloseVal = new Arraylist();


//lists from above contain column 0 and 1 from CSV:
while((reader.readLine()) != null) {

Sting line = "";
String ColTwo [] = line.split(",");
openVal.add(colOne[1]);
closVal.add(colOne[2]);

(2) 为更清楚起见,CSV [colOne [1], colOne [2] 的每一列都包含以下信息:

//colOne [1]  colOne [2]
   date        value
   friday       32
   tues         21
   wed          5

(3) 我想对它进行排序的方式是这样的(按值):

//colOne [1]  colOne [2]
   date        value
   wed          5
   tues         21
   friday       32

(4) 我发现比较器类效率不高,因为我不需要将信息写入数组列表的构造函数。该列表以 CSV 为前缀。

(3) 比较这两个列表的最佳方法是什么?

【问题讨论】:

  • @CrazySabbath 他们恰好在这种情况下。
  • 你想如何排序?价值观是什么?到目前为止,我只建议遍历列表的大小,并比较它们的每个元素,因为您不提供数据,也不提供您想要的排序方式。
  • 您希望它们如何排序?
  • 价值是什么意思?你能清楚地解释你想要的结果吗?
  • @CrazySabbath 我希望第二行的值数字对应于第一行的日期值,以便我可以对第二个值进行排序并确保它正确链接到第一列日期。

标签: java arrays sorting arraylist


【解决方案1】:

如果您的 csv 每个日期只包含一行,您可以将数据存储到地图而不是列表:

Map<String,Integer> myMap = new HashMap<>();
String line;
while((line = reader.readLine()) != null) {
    myMap.put(line.split(",")[0], Integer.parseInt(line.split(",")[1]));
}

之后您可以对地图进行排序:

Map<String,Integer> sorted = myMap.entrySet().stream().
                             sorted(Map.Entry.comparingByValue()).
                             collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(e1, e2) -> e1,LinkedHashMap::new));

并打印您的排序地图:

sorted.entrySet().forEach(System.out::println);

或者正如 DodgyCodeException 评论的那样,将您的行读给List&lt;String[]&gt;

List<String[]> myList = new ArrayList<>();
    String line = "";
    while((line = reader.readLine()) != null) {
        myList.add(line.split(","));
}

然后排序:

    Collections.sort(myList, new Comparator<String[]>() {
        @Override
        public int compare(String[] o1, String[] o2) {
            return Integer.compare(Integer.parseInt(o1[1]), Integer.parseInt(o2[1]));
        }
    });

最后打印您的列表只需使用 for 循环,例如:

for(String[] row : myList){
    System.out.println(row[0] +" : "+ row[1])
}

【讨论】:

  • 如何将其打印到控制台?会是 System.out.println(已排序)吗? ?
  • @DodgyCodeException 谢谢。使用 LinkedHashMap 编辑我的答案以保留顺序。
  • 这在大多数情况下都有效 - 即只要 Integer.parseInt(o1[1])- Integer.parseInt(o2[1]) 不会导致算术溢出。这就是为什么有一个方法Integer.compare(int a, int b)
  • 感谢@DodgyCodeException 和@Eritrean 的回答!为了清楚起见,当您提到 return Integer.parseInt(o1[1])- Integer.parseInt(o2[1]); [1] 是指初始 CSV 中的列行吗?如果是这种情况,不应该是 [1] - [2] 吗?
  • @Leemi 不,[1] 指的是第 2 列,即“值”列。您想比较两行 o1 和 o2 的值字段。每行都是一个 2 元素数组 (String[2]),其中 [0] 是第 1 列(日期),[1] 是第 2 列(值)。答案的最后一行应该是return Integer.compare(Integer.parseInt(o1[1]), Integer.parseInt(o2[1]));
【解决方案2】:

我能想到两种比解析成单独的面向列的数组更好的方法。

  • 编写一个包含成员字段的类来保存您的值,并使用其单一方法compareTo 实现Comparable 接口。将您的对象实例化并收集到List,然后调用Collections.sort。如果您有进一步的工作要做,或者如果您有额外的业务逻辑要放在某个地方,这种方法是最好的。
  • 使用库来解析 CSV 文件,并编写一些代码来对生成的元组进行排序。

我会展示第二个。

Apache Commons CSV 库

Apache Commons CSV 库对我来说在读写Comma-Separated ValuesTab-Delimited 文件方面效果很好。令人惊讶的是,这些格式有多种变体。 Commons CSV 至少可以处理其中的九个。

首先,创建一个示例数据文件。我冒昧地将第一列的标题从 date 更改为 dow,以便更准确地说是 day-of-week

dow,value
friday,32
wed,5
tues,21

Commons CSV 库返回 IterableCSVRecord 对象。 CSV 包含已解析 CSV 数据的一行中每一列的值。所以我们可以将这些对象用作tuple

List.sort 静态方法将为我们收集的元组(CSVRecord 对象)进行排序。我们需要提供一个Comparator 方法来对每个元组进行比较。对于每个元组,我们提取数据文件第一行中定义的第二个column by its name,即数据文件中的名称value

List < CSVRecord > tuples = new ArrayList <>( 3 );
Reader reader = null;
try {
    reader = new FileReader( "/Users/basilbourque/data.csv" );
    CSVFormat csvFormat = CSVFormat.RFC4180.withIgnoreSurroundingSpaces( true ).withHeader();
    Iterable < CSVRecord > iterable = csvFormat.parse( reader );
    // Convert `Iterable` to a `List`. 
    for ( CSVRecord record : iterable ) {
        tuples.add( record );
    }
} catch ( FileNotFoundException e ) {
    e.printStackTrace();
} catch ( IOException e ) {
    e.printStackTrace();
}

Comparator < CSVRecord > comparator = new Comparator < CSVRecord >() {
    @Override
    public int compare ( CSVRecord o1 , CSVRecord o2 ) {
        Integer a = Integer.valueOf( o1.get( "value" ) );
        Integer b = Integer.valueOf( o2.get( "value" ) );
        return a.compareTo( b );
    }
};
System.out.println( "tuples before sort: \n" + tuples );
tuples.sort( comparator );
System.out.println( "tuples after sort: \n" + tuples );

排序前的元组:

[CSVRecord [comment=null, mapping={dow=0, value=1}, recordNumber=1, values=[friday, 32]], CSVRecord [comment=null, mapping={dow=0, value= 1}, recordNumber=2, values=[wed, 5]], CSVRecord [comment=null, mapping={dow=0, value=1}, recordNumber=3, values=[tues, 21]]]

排序后的元组:

[CSVRecord [comment=null, mapping={dow=0, value=1}, recordNumber=2, values=[wed, 5]], CSVRecord [comment=null, mapping={dow=0, value= 1}, recordNumber=3, values=[tues, 21]], CSVRecord [comment=null, mapping={dow=0, value=1}, recordNumber=1, values=[friday, 32]]]

最后,循环现在排序的元组列表,使用CSVRecord::get 提取数据用于其他目的。

【讨论】:

    猜你喜欢
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 2023-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多