Tengo una lista de objetos, digamos formas. Me gustaría procesarlos usando una secuencia y devolver otro objeto, ShapeType, en función de lo que está en la lista.

Normalmente solo devolveré ShapeType.GENERIC, sin embargo, si hay un Rectángulo allí, me gustaría devolver ShapeType.RECT. Si hay un hexágono en una lista, me gustaría devolver ShapeType.HEXA. Cuando tanto el rectángulo como el cuadrado están presentes, me gustaría devolver ShapeType.HEXA.

Ahora, cuando se trata de código, me gustaría algo como esto:

  public ShapeType resolveShapeType(final List<Shape> shapes) {
    shapes.stream()
    .filter(shape -> shape.getSideCount() == 6 || shape.getSideCount() == 4)
    // I should have a stream with just rectangles and hexagons if present.
    // what now?
  }
2
Riv 3 ene. 2017 a las 20:13

5 respuestas

La mejor respuesta

Puede usar

public ShapeType resolveShapeType(final List<Shape> shapes) {
    int sides = shapes.stream()
        .mapToInt(Shape::getSideCount)
        .filter(count -> count==4 || count==6)
        .max().orElse(0);
    return sides==6? ShapeType.HEXA: sides==4? ShapeType.RECT: ShapeType.GENERIC;
}

Esto asigna cada elemento a su recuento lateral y los reduce al tipo preferido, que aquí es el recuento máximo, por lo que no se necesita una función de reducción personalizada.

Esto no es un cortocircuito, pero para la mayoría de los casos de uso, será suficiente. Si desea reducir el número de operaciones al mínimo necesario, las cosas serán más complicadas.

public ShapeType resolveShapeType(final List<Shape> shapes) {
    OptionalInt first = IntStream.range(0, shapes.size())
        .filter(index -> {
            int count = shapes.get(index).getSideCount();
            return count == 6 || count == 4;
        })
        .findFirst();
    if(!first.isPresent()) return ShapeType.GENERIC;
    int ix = first.getAsInt(), count = shapes.get(ix).getSideCount();
    return count==6? ShapeType.HEXA: shapes.subList(ix+1, shapes.size()).stream()
        .anyMatch(shape -> shape.getSideCount()==6)? ShapeType.HEXA: ShapeType.RECT;
}

Sabemos que podemos detenernos en el primer HEXA, pero para evitar un segundo pase, es necesario recordar si ocurrió RECT para el caso de que no haya HEXA. Por lo tanto, esto busca el primer elemento que sea, RECT o HEXA. Si no hay ninguno, se devuelve GENERIC, de lo contrario, si el primero no era un HEXA, los elementos restantes se verifican para un elemento del tipo HEXA. Tenga en cuenta que para procesar el resto después del primer RECT, no se necesita filter, ya que está implícito que las formas que no son RECT ni HEXA no pueden cumplir condición.

Pero también debería ser obvio que este código, que trata de minimizar el número de cheques, es más difícil de leer que un bucle for equivalente.

5
Holger 3 ene. 2017 a las 19:22

También puede hacer algo como esto:

ShapeType r = shapes.stream()
        .map(s -> ShapeType.parse(s.getSides()))
        .filter(c -> c == ShapeType.Hexagon || c==ShapeType.Square)
        .max(ShapeType::compareTo)
        .orElse(ShapeType.Generic);

Aquí, me he tomado un poco de libertad con tu ShapeType:

enum ShapeType {
    Square(4), Hexagon(6), Generic(Integer.MAX_VALUE);
    int v;
    ShapeType(int v) {
      this.v = v;
    }
    static ShapeType parse(int v) {
      switch (v) {
        case 4: return Square;
        case 6: return Hexagon;
        default:
          break;
      }
      return Generic;
    }
    public String toString(){
      return Integer.toString(v);
    }
  }

TBH puede evitar la operación de análisis si agrega un método getShapeType() que devolvió el tipo correcto por tipo Derivado. Luego, la operación map() solo extraerá el tipo, por ejemplo .map(Shape::getShapeType).

.filter() encontrará el grupo que le interesa, la forma más grande se considera la etiqueta de la colección ...

1
Nim 3 ene. 2017 a las 17:52

Suena como un caso para 'reducir' o 'recolectar / max'.

Suponga que tiene un método que selecciona el tipo 'dominante' (puede ponerlo en una lambda, pero en mi humilde opinión, es más legible como método):

public class Util{
    public static ShapeType dominantType(ShapeType t1, ShapeType t2){
        if(t1==HEXA || t2==HEXA) return HEXA;
        else if (t1==RECTANGLE || t2==RECTANGLE) return RECTANGLE;
        else return GENERIC;
    }
}

Hay varias formas de usarlo, un ejemplo de reducción sería:

shapes.stream()
.filter(shape -> shape.getSideCount() == 6 || shape.getSideCount() == 4)
.map(shape -> shape.getSideCount()==6? HEXA:RECTANGLE)
.reduce( GENERIC,  Util::dominantType); 
 //   using GENERIC in case of empty list

También es posible que desee buscar en collectors.maxBy. Por cierto, sea cual sea el enfoque que adopte, piense un poco en el comportamiento en caso de una lista vacía ...

1
Pelit Mamani 3 ene. 2017 a las 18:54

Si entiendo lo que estás tratando de hacer, entonces puedes usar anyMatch. Me gusta,

public ShapeType resolveShapeType(final List<Shape> shapes) {
    if (shapes.stream().anyMatch(shape -> shape.getSideCount() == 6)) {
        return ShapeType.HEXA;
    } else if (shapes.stream().anyMatch(shape -> shape.getSideCount() == 4)) {
        return ShapeType.RECT;
    } else {
        return ShapeType.GENERIC;
    }
}

Una forma de hacer esto (transmitir shapes una vez) sería preservar la presencia de la forma con una matriz. Me gusta,

public ShapeType resolveShapeType(final List<Shape> shapes) {
    boolean[] bits = new boolean[2];
    shapes.stream().forEach(shape -> {
        int sides = shape.getSideCount();
        if (sides == 4) {
            bits[0] = true;
        } else if (sides == 6) {
            bits[1] = true;
        }
    });
    if (bits[1]) {
        return ShapeType.HEXA;
    } else if (bits[0]) {
        return ShapeType.RECT;
    } else {
        return ShapeType.GENERIC;
    }
}
2
Elliott Frisch 3 ene. 2017 a las 17:34

Suponiendo que solo los tres tipos de formas pueden estar presentes en la lista, una alternativa sería:

Set<Integer> sides = shapes.stream()
      .map(Shape::getSideCount)
      .collect(toSet());

if (sides.contains(6)) return HEXA;
else if (sides.contains(4)) return RECTANGLE;
else return GENERIC;

Pero creo que la forma más directa (y eficiente) sería un buen bucle antiguo:

ShapeType st = GENERIC;
for (Shape s : shapes) {
  if (s.getSideCount() == 6) return HEXA;
  if (s.getSideCount() == 4) st = RECTANGLE;
}
return st;
4
assylias 3 ene. 2017 a las 17:35