JavaRush /Java блогу /Random-KY /Java 8 Функциялары – Акыркы колдонмо (2-бөлүк)
0xFF
Деңгээл
Донецк

Java 8 Функциялары – Акыркы колдонмо (2-бөлүк)

Группада жарыяланган
Макаланын котормосунун экинчи бөлүгү Java 8 Features – The ULTIMATE Guide . Биринчи бөлүгү бул жерде (шилтеме өзгөрүшү мүмкүн). Java 8 Функциялары – Акыркы колдонмо (2-бөлүк) - 1

5. Java 8 китепканаларындагы жаңы мүмкүнчүлүктөр

Java 8 көптөгөн жаңы класстарды кошту жана заманбап параллелдүүлүктү, функционалдык программалоону, дата/убакытты жана башкаларды жакшыраак колдоо үчүн учурдагы класстарды кеңейтти.

5.1. Класс Кошумча

Атактуу NullPointerException Java тиркемесинин каталарынын эң көп таралган себеби болуп саналат. Бир топ убакыт мурун, Google'дун GuavaOptional мыкты долбоору чечим катары сунушталган NullPointerException, ошону менен codeдун нөл чектер менен булганышына жол бербейт жана натыйжада таза codeду жазууга үндөгөн. Google шыктандырган Guava классы Optionalазыр Java 8дин бир бөлүгү. OptionalБул жөн гана контейнер: ал маанини же кандайдыр бир түрүн камтышы мүмкүн Тже жөн эле нөл болушу мүмкүн. Бул ачык-айкын нөл текшерүүлөр мындан ары акталышы үчүн көптөгөн пайдалуу ыкмаларды камсыз кылат. Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз . Келгиле, колдонуунун эки кичинекей мисалын карап көрөлү Optional: нөл менен жана жок.
Optional<String> fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
Метод эгерде инстанцияда нөл эмес маани камтылса, чындыктыisPresent() кайтарат , башка учурда жалган . Метод демейки маанини түзүү үчүн функцияларды кабыл алып, нөлдү камтыса, натыйжанын кайра механизмин камтыйт . map () ыкмасы учурдагы маанини өзгөртүп , жаңы инстанцияны кайтарат . Метод окшош , бирок функциянын ордуна ал демейки маанини алат. Бул жерде бул программанын жыйынтыгы болуп саналат: OptionalorElseGet()OptionalOptionalOptionalorElse()orElseGet()
Full Name is set? false
Full Name: [none]
Hey Stranger!
Келгиле, дагы бир мисалды карап көрөлү:
Optional<String> firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
Натыйжа мындай болот:
First Name is set? true
First Name: Tom
Hey Tom!
Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз .

5.2. Агымдар

Жаңыдан кошулган Stream API ( java.util.stream) Java тorнде чыныгы функционалдык стилдеги программалоону киргизет. Бул Java китепканасына эң кеңири кошумча болуп саналат жана Java иштеп чыгуучуларына кыйла натыйжалуураак болууга мүмкүндүк берет, ошондой эле аларга эффективдүү, таза жана кыска codeду түзүүгө мүмкүндүк берет. Stream API жыйнактарды иштетүүнү бир топ жеңилдетет (бирок алар менен эле чектелбейт, биз кийинчерээк көрөбүз). Мисал катары жөнөкөй классты алалы Task.
public class Streams  {
    private enum Status {
        OPEN, CLOSED
    };

    private static final class Task {
        private final Status status;
        private final Integer points;

        Task( final Status status, final Integer points ) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format( "[%s, %d]", status, points );
        }
    }
}
Тапшырмада кандайдыр бир пункттар (же псевдо-кыйынчылыктар) бар жана АЧЫК же ЖАБУУ болушу мүмкүн . Келгиле, ойной турган көйгөйлөрдүн чакан жыйнагын киргизели.
final Collection<Task> tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 )
);
Биз билгибиз келген биринчи суроо - АЧУУ тапшырмалары учурда канча пунктту камтыйт? Java 8ге чейин, бул үчүн кадимки чечим итераторду колдонуу болмок foreach. Бирок Java 8де жооп агым болуп саналат: ырааттуу жана параллелдүү агрегаттык операцияларды колдогон элементтердин ырааттуулугу.
// Подсчет общего количества очков всех активных задач с использованием sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
Жана консолдун чыгышы төмөнкүдөй болот:
Total points: 18
Келгиле, бул жерде эмне болуп жатканын карап көрөлү. Биринчиден, тапшырмалар жыйнагы агымдык өкүлчүлүккө айландырылат. Андан кийин операция ЖАБЫКfilter статусу менен бардык тапшырмаларды чыпкалайт . Кийинки кадамда операция ар бир инстанция үчүн ыкманы колдонуу менен s агымын s агымына айлантат . Акыр-аягы, бардык упайлар акыркы натыйжаны камсыз кылуучу метод аркылуу жыйынтыкталат . Кийинки мисалдарга өтүүдөн мурун, эстен чыгарбоо керек болгон жиптер жөнүндө кээ бир эскертүүлөр бар (көбүрөөк маалымат бул жерде ). Операциялар аралык жана акыркы операциялар болуп бөлүнөт . Аралык операциялар жаңы агымды кайтарат. Алар ар дайым жалкоо; сыяктуу аралык операцияларды аткарууда чындыгында чыпкалоону жүргүзбөйт, анын ордуна жаңы агым түзүшөт, ал аяктагандан кийин берилген предикатка дал келген баштапкы агымдын элементтерин камтыйт. жана сыяктуу чектүү операцияларды агым аркылуу өткөрүп, натыйжаны же терс таасирди жаратууга болот. Акыркы операция аяктагандан кийин, агым колдонулган деп эсептелет жана аны кайра колдонууга болбойт. Дээрлик бардык учурларда, акыркы операциялар негизги маалымат булагы аркылуу өтүүнү аягына чыгарышат. Жиптердин дагы бир баалуу өзгөчөлүгү - бул кутудан тышкары параллелдүү процесстерди колдоо. Бардык маселелердин упайларынын суммасын тапкан бул мисалды карап көрөлү. mapToIntTaskIntegerTask::getPointsTasksumstreamfilterforEachsum
// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints )
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );
Бул биринчи мисалга абдан окшош, бирок биз бардык тапшырмаларды параллелдүү иштеп чыгууга жана акыркы натыйжаны метод аркылуу эсептөөгө аракет кылып жатабыз reduce. Бул жерде консолдун натыйжасы:
Total points (all tasks): 26.0
Көп учурда белгилүү бир критерий боюнча элементтерди топтоого муктаждык бар. Мисал жиптер буга кандайча жардам берерин көрсөтүп турат.
// Группировка задач по их статусу
final Map<Status, List<Task>> map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
Консолдун чыгышы төмөнкүдөй болот:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
Маселенин мисалдары менен бүтүрүү үчүн, жалпы упайлардын негизинде жыйнактагы ар бир маселенин жалпы пайызын (же салмагын) эсептеп көрөлү:
// Подсчет веса каждой задачи (How процент от общего количества очков)
final Collection<String> result = tasks
    .stream()                                        // Stream<String>
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream<Double>
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream<String>
    .collect( Collectors.toList() );                 // List<String>

System.out.println( result );
Консолдун чыгышы төмөнкүдөй болот:
[19%, 50%, 30%]
Акырында, биз мурда белгилегендей, Stream API Java жыйнактары үчүн гана эмес. Тексттик файлдарды сап боюнча окуу сыяктуу типтүү киргизүү/чыгаруу операциясы агымдык процессти колдонуу үчүн абдан жакшы талапкер. Муну далилдөө үчүн кичинекей бир мисал келтирели.
final Path path = new File( filename ).toPath();
try( Stream<String> lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
onConsoleЖипте чакырылган ыкма кошумча жеке иштеткич менен эквиваленттүү жипти кайтарат. close()Метод жипте чакырылганда жеке иштеткич чакырылат . Stream API, ламбдалар жана маалымдама ыкмалары , ошондой эле Java 8деги демейки жана статикалык методдор заманбап программалык камсыздоону иштеп чыгуу парадигмаларына жооп болуп саналат. Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз .

5.3. Date/Time API (JSR 310)

Java 8 жаңы Date and Time API (JSR 310) менен камсыз кылуу менен дата жана убакытты башкаруунун жаңы көрүнүшүн алып келет . Дата жана убакыт манипуляциясы Java иштеп чыгуучулары үчүн эң начар көйгөйлөрдүн бири. java.util.DateТөмөнкү стандарт java.util.Calendarабалды жалпысынан жакшырта алган жок (балким, аны ого бетер чаташтырып жибериши мүмкүн). Ошентип Joda-Time пайда болду: Java үчүн сонун дата/убакыт API альтернативасы . Java 8деги жаңы Date/Time API (JSR 310) Joda-Time тарабынан катуу таасир этет жана андан эң жакшысын алат. Жаңы топтом дата, убакыт, дата/убакыт, убакыт алHowтары, узактыктар жана убакытты башкаруу үчүн бардык класстардыjava.time камтыйт . API дизайны өзгөрүлбөстүктү абдан олуттуу кабыл алды: өзгөртүүлөргө жол берилбейт (бул оор сабак ). Эгерде өзгөртүү талап кылынса, тиешелүү класстын жаңы нускасы кайтарылат. Келгиле, аларды колдонуунун негизги класстарын жана мисалдарын карап көрөлү. Убакыт алкагын колдонуу менен учурдагы заматта, датага жана убакытка жетүүнү камсыз кылган биринчи класс . жана ордуна колдонулушу мүмкүн . java.util.CalendarClockClockSystem.currentTimeMillis()TimeZone.getDefault()
// Получить системное время How смещение UTC
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
Мисал консол чыгаруу:
2014-04-12T15:19:29.282Z
1397315969360
Биз карай турган башка жаңы класстар LocaleDateжана LocalTime. LocaleDateISO-8601 календардык тутумунда убакыт алкагы жок дата бөлүгүн гана камтыйт. Демек, LocalTimeал timecode> бир гана бөлүгүн камтыйт.
// получить местную date и время время
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// получить местную date и время время
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );
Мисал консол чыгаруу:
2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
LocalDateTimeбириктирет LocaleDateжана LocalTimeжана ISO-8601 календардык тутумунда күн менен убакытты камтыйт, бирок убакыт алкагы жок. Жөнөкөй мисал төмөндө келтирилген.
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
Мисал консол чыгаруу:
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
Эгер сизге белгилүү бир убакыт алкагы үчүн дата/убакыт керек болсо, ZonedDateTime. Ал ISO-8601 календардык тутумунда дата менен убакытты камтыйт. Бул жерде ар кандай убакыт алHowтары үчүн кээ бир мисалдар келтирилген.
// Получение даты/времени для временной зоны
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
Мисал консол чыгаруу:
2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
Акыр-аягы, классты карап көрөлү Duration: секундалар жана наносекундалардагы убакыт аралыгы. Бул эки күндүн ортосундагы эсептөөнү абдан жөнөкөй кылат. Келгиле, муну кантип жасоону карап көрөлү:
// Получаем разницу между двумя датами
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
Жогорудагы мисал эки датанын, 2014-жылдын 16-апрелине жана 2015-жылдын 16-апрелине чейинки аралыкты (күн жана саат менен) эсептейт . Бул жерде консолдун чыгышынын мисалы:
Duration in days: 365
Duration in hours: 8783
Java 8деги жаңы дата/убакыт тууралуу жалпы таасир абдан, абдан оң. Жарым-жартылай өзгөртүүлөр согуштук сыноодон өткөн негизге (Joda-Time) негизделгендиктен, жарым-жартылай бул жолу маселе олуттуу түрдө кайра каралып, иштеп чыгуучулардын үнү угулгандыктан. чоо-жайын билүү үчүн, сураныч, расмий documentтерди карагыла .

5.4. Nashorn JavaScript кыймылдаткычы

Java 8 жаңы Nashorn JavaScript кыймылдаткычы менен келет , ал JVMде JavaScript тиркемелеринин айрым түрлөрүн иштеп чыгууга жана иштетүүгө мүмкүндүк берет. Nashorn JavaScript кыймылдаткычы Java жана JavaScriptтин өз ара аракеттенүүсүнө уруксат берүү үчүн бирдей эрежелердин топтомун карманган javax.script.ScriptEngine программасынын дагы бир ишке ашырылышы. Бул жерде кичинекей бир мисал.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );

System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );
Мисал консол чыгаруу:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

5.5. База 64

Акырында, Base64 codeдоосун колдоо Java 8дин чыгышы менен Java стандарттык китепканасына кирди. Аны колдонуу абдан оңой, муну мисал көрсөтүп турат.
package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
    public static void main(String[] args) {
        final String text = "Base64 finally in Java 8!";

        final String encoded = Base64
            .getEncoder()
            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println( encoded );

        final String decoded = new String(
            Base64.getDecoder().decode( encoded ),
            StandardCharsets.UTF_8 );
        System.out.println( decoded );
    }
}
Программанын консолдук чыгарылышы codeдолгон жана чечмеленген текстти көрсөтөт:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
Ошондой эле URL-достук codeерлер/деcodeерлер, ошондой эле MIME-достук codeдоочулар/деcodeерлер ( Base64.getUrlEncoder()/ Base64.getUrlDecoder(), Base64.getMimeEncoder()/ Base64.getMimeDecoder()) үчүн класстар бар.

5.6. Параллель массивдер

Java 8 чыгаруу параллелдүү массивдерди иштетүү үчүн көптөгөн жаңы ыкмаларды кошот. Балким, алардын эң маанилүүсү parallelSort(), көп ядролуу машиналарда сорттоону тездетет. Төмөндөгү кичинекей мисал ыкмалардын жаңы үй-бүлөсүн ( parallelXxx) иш жүзүндө көрсөтөт.
package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
    public static void main( String[] args ) {
        long[] arrayOfLong = new long [ 20000 ];

        Arrays.parallelSetAll( arrayOfLong,
            index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
            i -> System.out.print( i + " " ) );
        System.out.println();

        Arrays.parallelSort( arrayOfLong );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
            i -> System.out.print( i + " " ) );
        System.out.println();
    }
}
Коддун бул кичинекей бөлүгү parallelSetAll()массивди 20 000 кокустук маанилери менен толтуруу ыкмасын колдонот. Андан кийин ал колдонулат parallelSort(). Программа массив чындыгында сорттолгондугун көрсөтүү үчүн сорттоого чейин жана андан кийин алгачкы 10 элементти басып чыгарат. Мисал программанын чыгарылышы мындай болушу мүмкүн (массивдин элементтери кокустук экенин эске алыңыз).
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

5.7. Параллелизм

java.util.concurrent.ConcurrentHashMapЖаңы кошулган агым an objectилерине жана ламбда туюнтмаларына негизделген агрегаттык операцияларды колдоо үчүн класска жаңы методдор кошулду . java.util.concurrent.ForkJoinPoolОшондой эле класска жалпы бириктирүүнү колдоо үчүн жаңы ыкмалар кошулду (ошондой эле Java параллелдүүлүк боюнча акысыз курсубузду караңыз ). Окуу/жазууну башкаруу үчүн үч кирүү режими менен мүмкүнчүлүккө негизделген кулпулоону камсыз кылуу үчүн жаңы класс java.util.concurrent.locks.StampedLockкошулду (аны анчалык деле жакшы эмес альтернатива катары караса болот java.util.concurrent.locks.ReadWriteLock). Пакетке кошулган жаңы класстар java.util.concurrent.atomic:
  • DoubleAkumulyator
  • DoubleAdder
  • Узун аккумулятор
  • LongAdder

6. Java иштөө чөйрөсүндөгү жаңы мүмкүнчүлүктөр (JVM)

Аймак PermGenиштен чыгарылып, анын ордуна Metaspace (JEP 122) орнотулган. JVM параметрлери -XX:PermSizeжана тиешелүүлүгүнө -XX:MaxPermSizeжараша алмаштырылган . -XX:MetaSpaceSize-XX:MaxMetaspaceSize

7. Корутунду

Келечек бул жерде: Java 8 иштеп чыгуучуларга көбүрөөк жемиштүү болууга мүмкүндүк берген функцияларды жеткирүү менен платформасын алдыга жылдырды. Өндүрүштүк системаларды Java 8ге көчүрүү али эрте, бирок кабыл алуу кийинки бир нече айдын ичинде акырындык менен өсө башташы керек. Бирок, азыр Java 8 шайкештиги үчүн code базасын даярдап баштоого жана ал жетиштүү коопсуз жана туруктуу болгондо Java 8 өзгөртүүлөрдү киргизүүгө даяр болууга убакыт келди. Коомчулуктун Java 8ди кабыл алгандыгынын далor катары, Pivotal жакында Java 8 үчүн өндүрүштүк колдоо менен Spring Framework чыгарды . Сиз комментарийлерде Java 8деги кызыктуу жаңы функциялар жөнүндө өзүңүздүн салымыңызды бере аласыз.

8. Булактар

Java 8 өзгөчөлүктөрүнүн ар кандай аспектилерин терең талкуулаган кээ бир кошумча ресурстар:
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION