LinkedList 和 ArrayDeque的性能分析( 四 )


For such kind of data, LinkedList is a good solution - we can remove elements from any position of the list quite cheap. First of all we need a method to extract events for particular IP address from the network log. Do not try to select elements and remove them from the list afterwards, even if you already have one of these methods in your library, because it would require 2 iterations instead of one for extraction. We have to use ListIterator in the extraction method because this is the only way to iterate elements and remove some of them from the source list at the same time.
private static final class LogEvent{ public final int ipv4; public final long time; public final String eventDesc; private LogEvent( final int ipv4, final long time, final String eventDesc ) { this.ipv4 = ipv4; this.time = time; this.eventDesc = eventDesc; }} private static List\u0026lt;LogEvent\u0026gt; extractForIp( final LinkedList\u0026lt;LogEvent\u0026gt; fullLst, final int ip ){ final List\u0026lt;LogEvent\u0026gt; res = new ArrayList\u0026lt;LogEvent\u0026gt;( 10 ); final ListIterator\u0026lt;LogEvent\u0026gt; iter = fullLst.listIterator(); while ( iter.hasNext() ) { final LogEvent event = iter.next(); if ( event.ipv4 == ip ) { res.add( event ); iter.remove(); } } return res;} Now we are able to process all IP addresses in the current timestamp.
private static void processIp( final List\u0026lt;LogEvent\u0026gt; lst ){ //...processing logic here} private static void processFirstTimestamp( final LinkedList\u0026lt;LogEvent\u0026gt; fullList ){ if ( fullList.isEmpty() ) return; final long firstTime = fullList.getFirst().time; while ( !fullList.isEmpty() \u0026amp;\u0026amp; fullList.getFirst().time == firstTime ) { final int ip = fullList.getFirst().ipv4; final List\u0026lt;LogEvent\u0026gt; eventsForIp = extractForIp( fullList, ip ); processIp( eventsForIp ); }} Unfortunately, this algorithm will perform poorly when you have a lot of messages/IP addresses in your log. Each time you have to scan all events but you will end up with only a few messages after extraction. The better way is to maintain a map from IP addresses to lists of their events. Both extraction and appending would work much faster in this case. We will use LinkedHashMapin order to keep original order of found IP. If we have removed all entries for some IP address and added some new entries for the same IP address later, this IP address would be added to the tail of iteration order. updateMap method adds all entries from the input list to the map and cleans input list. We need only this method for both initial and subsequent calls.
private static Map\u0026lt;Integer, List\u0026lt;LogEvent\u0026gt;\u0026gt; extractMap( final List\u0026lt;LogEvent\u0026gt; fullLst ){ final Map\u0026lt;Integer, List\u0026lt;LogEvent\u0026gt;\u0026gt; res = new LinkedHashMap\u0026lt;Integer, List\u0026lt;LogEvent\u0026gt;\u0026gt;( 10 ); updateMap( res, fullLst ); return res;} private static void updateMap( final Map\u0026lt;Integer, List\u0026lt;LogEvent\u0026gt;\u0026gt; eventMap, final List\u0026lt;LogEvent\u0026gt; fullLst ){ for ( final LogEvent event : fullLst ) { List\u0026lt;LogEvent\u0026gt; lst = eventMap.get( event.ipv4 ); if ( lst == null ) { lst = new ArrayList\u0026lt;LogEvent\u0026gt;( 10 ); eventMap.put( event.ipv4, lst ); } lst.add( event ); } fullLst.clear();}


推荐阅读