2016-07-12 6 views
5

스트림을 사용하여 너무 우아하지 않은 코드를 리펙토링하려고합니다. 내가 HashMap의 문자열과 MyObjects를 포함, 현재과 같이 루프를 사용하여 반복 있습니다스트림을 사용하여 그룹화를 기반으로 컬렉션에서 두 가지 기능을 수행하려면 어떻게해야합니까?

Map<String, MyObject> map = new HashMap<>(); 
Map<String, MyObject> objectsToAdd = new HashMap<>(); 


for(MyObject object : map.values()){ 
     String idToAdd = object.getConnectedToId(); 

     if(StringUtils.isEmpty(idToAdd) { 
      continue; 
     } 

     if(idToAdd.substring(0,1).equals("i")){ // connected to an ICS 
      MyObject newObject = service1.someMethod(idToAdd); 

      if(newObject != null) { 
       objectsToAdd.put(newObject.getId(), newObject); 
      } 
     } else if (idToAdd.substring(0,1).equals("d")){ // connected to a device 
      MyObject newObject = service2.someMethod(idToAdd); 
      if(newObject != null) { 
       objectsToAdd.put(newObject.getId(), newObject); 
      } 
     } 

    } 

    map.putAll(objectsToAdd); 

나는 단지 ID에 대한 걱정 때문에를, 나는 뒤에 만 ID를 얻기 위해지도 작업을 사용하여 시작 빈 것을 제거하는 필터 조작.

다음 부분은 내가 문제가되는 부분입니다. 이 링크는 스트림 수집을 사용 감소에 도움이

 map.values().stream() 
      .map(myObject -> myObject.getConnectedToId()) // get a map of all the ids 
      .filter(StringUtils::isNotEmpty) // filter non empty ones 
      .collect(
       Collectors.mapping(
        MyObject::getId, 
        Collectors.toList())), 
         Collectors.groupingBy(
          s -> s.substring(0,1)); 

: 나는 ID의 첫 번째 문자를 기반으로 항목과 나는이와 결국 그룹 수 나는 수집기, groupingBy 작업을 사용하여 한 시도 우선 그래서 : Stream Reduction

이 코드에는 적어도 다음 두 가지 문제가 있습니다. 1) 수집은 스트림을 닫고 아직 완료하지 않았으며 2) 원래 개체가 필요하지만 이제는 connectedToIds의 map입니다.

Q1) ID의 첫 문자를 기준으로 개체를 그룹화 할 수있는 중간 작업이 있습니까?

Q2) 컬렉션을 ID로만 줄이지 않고 어떻게 할 수 있습니까?

Q3) 그리고 마지막으로 컬렉션이 그룹화되면 (두 개가 될 것입니다.) 원래 코드에서와 같이 각 그룹별로 어떻게 별도의 기능을 수행 할 수 있습니까?


최종 솔루션 (덕분에 도움을 & @Flown을 @Holger하기는)

Map<Character, Function<String, MyObejct>> methodMapping = new HashMap<>(); 
    methodMapping.put('i', service1::method1); 
    methodMapping.put('d', service2::method2); 

    Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId) 
     .filter(StringUtils::isNotEmpty) 
     .map(id -> methodMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
     .filter(Objects::nonNull) 
     .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2)); 

    map.putAll(toAdd); 

동시 수정 예외를 방지하기 위해, 먼저 저장소에 임시 맵에있는 오브젝트를 수행하는 동안 필요 스트림 작업을 완료 한 다음 최종 맵에 추가하십시오.

+1

지도의 키가 'MyObject'의 'id'라고 가정합니다. 나는. – Flown

+0

예, HashMap의 키는 MyObject의 ID입니다. 명확성을 위해 질문에 대한 사소한 편집을했습니다. idToAdd는 객체의 ID가 아니라 myObject의 connectedToId입니다. – Kristina

답변

3

Stream 접근 방식과 일반적인 접근 방식은 반환 유형 측면에서 매우 다릅니다. 따라서 저는 이전 접근 방식을 Stream API로 변경했습니다.

코드를 줄이려면 매핑 단계에서 간결한 조회를 수행하기 위해 먼저 Map<Character, Function<String, MyObject>>을 만들어야합니다.
다음과 같은 :

파이프 라인의 작업을 수행하는 방법
Map<Character, Function<String, MyObject>> serviceMapping = new HashMap<>(); 
serviceMapping.put('i', service1); 
serviceMapping.put('d', service2); 

?

  1. 지도 MyObject ->MyObject::getConnectedToId
  2. Strings
  3. 빈은 serviceMap에서 룩업을 수행하는 필터.존재하는 경우, 그 바로 추출 기능

Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId) 
    .filter(StringUtils::isEmpty) 
    .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
    .filter(Objects::nonNull) 
    .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2)); 
map.putAll(toAdd); 

이 제공함으로써 Function<String, MyObject> 밖의 id -> null

  • 필터를
  • 마지막 단계는 상기 결과를 수집하는 것이다 null 값을 반환 연산을 사용하여 계산 된 값을 map에 직접 더할 수도 있습니다.

    map.values().stream().map(MyObject::getConnectedToId) 
        .filter(StringUtils::isEmpty) 
        .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
        .filter(Objects::nonNull) 
        .forEach(mo -> map.put(mo.getId(), mo)); 
    
  • +0

    mo1과 mo2는 method1과 method2로 가정됩니까? 또한 마지막 수집 작업에서 "정적이 아닌 메서드를 정적 컨텍스트에서 참조 할 수 없습니다." – Kristina

    +2

    @Kristina 이미 존재하는 값이 충돌하면 기본 'Collectors :: toMap'이 예외를 throw합니다. . 따라서 어떤 요소가 취해 졌는지, 새로운 값 또는 이전 값 ('(mo1, mo2) -> mo2')을 결정하는 역할을하는 병합 함수를 제공해야합니다. – Flown

    +0

    @Holger'map' 연산이 실행될 때까지 모든 것. MyObject를 반환하는 함수를 원하기 때문에'serviceMapping'을 한 번 수정해야했습니다. 'Map <문자, 함수 > methodMapping = new HashMap <>(); methodMapping.put ('i', service1 :: getMOById); methodMapping.put ('d', service2 :: getMOById); ' 이 모양이 맞습니까? 메소드는이 경우에 호출되지 않는 대신'map' 연산은 아무런 결과도 반환하지 않습니다. – Kristina

    관련 문제