I'm probably ruining some students’ homework assignment, but here goes…
As suggested on the Question, the natural way in Java is to create a class to represent your data. Then implement a Comparator
to be passed to the utility method Collections.sort
.
On my MacBook Pro 2.3 GHz Intel Core i7 running a Parallels virtual machine with Java 8, a data set of 42,000 elements takes between 45-90 milliseconds to sort.
I changed your example data to be more interesting, introducing some varied dates and duplicate latitudes.
20000101,34.6920,-116.3550,12.30,1.21
20000101,34.4420,-116.2280,7.32,1.01
20000101,34.6920,-121.7667,5.88,1.14
20000101,-41.1300,174.7600,27.00,1.90
20000101,37.6392,-119.0482,2.40,1.03
20000101,32.1790,-115.0730,6.00,2.44
20000101,34.6920,-152.2192,86.34,1.48
20000102,34.6320,-116.2410,11.63,1.61
20000102,59.5369,-153.1360,100.15,1.62
20000102,44.7357,-110.7932,4.96,2.20
20000102,34.6320,-116.2950,9.00,1.73
20000102,34.6320,-110.7938,5.32,1.75
20000102,34.6320,-117.6320,4.15,1.45
20000102,41.9270,20.5430,10.00,4.80
My GeoReading
class to represent the data.
class GeoReading
{
LocalDate localDate = null;
BigDecimal latitude = null;
BigDecimal longitude = null;
BigDecimal depth = null;
BigDecimal magnitude = null;
public GeoReading( String arg )
{
// String is comma-separated values of: Date,Lat,Lon,Depth,Mag
List<String> items = Arrays.asList( arg.split( "\\s*,\\s*" ) ); // Regex explained here: http://stackoverflow.com/a/7488676/642706
this.localDate = ISODateTimeFormat.basicDate().parseLocalDate( items.get( 0 ) );
this.latitude = new BigDecimal( items.get( 1 ) );
this.longitude = new BigDecimal( items.get( 2 ) );
this.depth = new BigDecimal( items.get( 3 ) );
this.magnitude = new BigDecimal( items.get( 4 ) );
}
@Override
public String toString()
{
return "GeoReading{" + "localDate=" + localDate + ", latitude=" + latitude + ", longitude=" + longitude + ", depth=" + depth + ", magnitude=" + magnitude + '}';
}
}
Here is the Comparator implementation.
class GeoReadingAscendingComparator implements Comparator<GeoReading>
{
@Override
public int compare( GeoReading o1 , GeoReading o2 )
{
int localDateCompare = o1.localDate.compareTo( o2.localDate );
if ( localDateCompare != 0 ) { // If not equal on this component, so compare on this.
return localDateCompare;
}
int latitudeCompare = o1.latitude.compareTo( o2.latitude );
if ( latitudeCompare != 0 ) { // If not equal on this component, so compare on this.
return latitudeCompare;
}
return o1.longitude.compareTo( o2.longitude );
}
}
Main code.
Path path = Paths.get( "/Users/basil/lat-lon.txt" ); // Path for Mac OS X.
try {
List<GeoReading> list = new ArrayList<>();
Stream<String> lines = Files.lines( path );
lines.forEach( line -> list.add( new GeoReading( line ) ) );
// Take those 14 lines and multiply to simulate large text file. 14 * 3,000 = 42,000.
int count = 3000;
List<GeoReading> bigList = new ArrayList<>( list.size() * count ); // Initialze capacite to expected number of elements.
for ( int i = 0 ; i < count ; i++ ) {
bigList.addAll( list );
}
long start = System.nanoTime();
Collections.sort( bigList , new GeoReadingAscendingComparator() );
long elapsed = ( System.nanoTime() - start );
System.out.println( "Done sorting the GeoReading list. Sorting " + bigList.size() + " took: " + TimeUnit.MILLISECONDS.convert( elapsed , TimeUnit.NANOSECONDS ) + " ms ( " + elapsed + " nanos )." );
System.out.println( "Dump…" );
for ( GeoReading g : bigList ) {
System.out.println( g );
}
} catch ( IOException ex ) {
System.out.println( "ERROR - ex: " + ex );
}
In the real world I would add some defensive-programming code to verify the incoming data. Data from external sources is always defective and/or changing.